comctl32/tooltips: Remove redundant code, let handlers deal with A<->W conversions.
[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         tmp.x = x;
3624         tmp.y = y;
3625
3626         lvHitTestInfo.pt = tmp;
3627         LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3628
3629         if (infoPtr->bMarqueeSelect)
3630         {
3631             LVITEMW item;
3632             ITERATOR i;
3633
3634             if (x > infoPtr->ptClickPos.x)
3635             {
3636                 rect.left = infoPtr->ptClickPos.x;
3637                 rect.right = x;
3638             }
3639             else
3640             {
3641                 rect.left = x;
3642                 rect.right = infoPtr->ptClickPos.x;
3643             }
3644
3645             if (y > infoPtr->ptClickPos.y)
3646             {
3647                 rect.top = infoPtr->ptClickPos.y;
3648                 rect.bottom = y;
3649             }
3650             else
3651             {
3652                 rect.top = y;
3653                 rect.bottom = infoPtr->ptClickPos.y;
3654             }
3655
3656             /* Cancel out the old marquee rectangle and draw the new one */
3657             LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
3658
3659             /* Invert the items in the old marquee rectangle */
3660             iterator_frameditems(&i, infoPtr, &infoPtr->marqueeRect);
3661
3662             while (iterator_next(&i))
3663             {
3664                 if (i.nItem > -1)
3665                 {
3666                     if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED) == LVIS_SELECTED)
3667                         item.state = 0;
3668                     else
3669                         item.state = LVIS_SELECTED;
3670
3671                     item.stateMask = LVIS_SELECTED;
3672
3673                     LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3674                 }
3675             }
3676
3677             iterator_destroy(&i);
3678
3679             CopyRect(&infoPtr->marqueeRect, &rect);
3680
3681             /* Iterate over the items within our marquee rectangle */
3682             iterator_frameditems(&i, infoPtr, &rect);
3683
3684             while (iterator_next(&i))
3685             {
3686                 if (i.nItem > -1)
3687                 {
3688                     /* If CTRL is pressed, invert. If not, always select the item. */
3689                     if ((fwKeys & MK_CONTROL) && (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)))
3690                         item.state = 0;
3691                     else
3692                         item.state = LVIS_SELECTED;
3693
3694                     item.stateMask = LVIS_SELECTED;
3695
3696                     LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3697                 }
3698             }
3699
3700             iterator_destroy(&i);
3701
3702             LISTVIEW_InvalidateRect(infoPtr, &rect);
3703             return 0;
3704         }
3705
3706         rect.left = infoPtr->ptClickPos.x - wDragWidth;
3707         rect.right = infoPtr->ptClickPos.x + wDragWidth;
3708         rect.top = infoPtr->ptClickPos.y - wDragHeight;
3709         rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
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     INT i, j;
5008
5009     TRACE("()\n");
5010
5011     /* we do it directly, to avoid notifications */
5012     ranges_clear(infoPtr->selectionRanges);
5013     infoPtr->nSelectionMark = -1;
5014     infoPtr->nFocusedItem = -1;
5015     SetRectEmpty(&infoPtr->rcFocus);
5016     /* But we are supposed to leave nHotItem as is! */
5017
5018
5019     /* send LVN_DELETEALLITEMS notification */
5020     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5021     nmlv.iItem = -1;
5022     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
5023
5024     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
5025     {
5026         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5027         {
5028             /* send LVN_DELETEITEM notification, if not suppressed
5029                and if it is not a virtual listview */
5030             if (!bSuppress) notify_deleteitem(infoPtr, i);
5031             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
5032             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
5033             {
5034                 hdrItem = DPA_GetPtr(hdpaSubItems, j);
5035                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5036                 Free(hdrItem);
5037             }
5038             DPA_Destroy(hdpaSubItems);
5039             DPA_DeletePtr(infoPtr->hdpaItems, i);
5040         }
5041         DPA_DeletePtr(infoPtr->hdpaPosX, i);
5042         DPA_DeletePtr(infoPtr->hdpaPosY, i);
5043         infoPtr->nItemCount --;
5044     }
5045     
5046     if (!destroy)
5047     {
5048         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5049         LISTVIEW_UpdateScroll(infoPtr);
5050     }
5051     LISTVIEW_InvalidateList(infoPtr);
5052     
5053     return TRUE;
5054 }
5055
5056 /***
5057  * DESCRIPTION:
5058  * Scrolls, and updates the columns, when a column is changing width.
5059  *
5060  * PARAMETER(S):
5061  * [I] infoPtr : valid pointer to the listview structure
5062  * [I] nColumn : column to scroll
5063  * [I] dx : amount of scroll, in pixels
5064  *
5065  * RETURN:
5066  *   None.
5067  */
5068 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
5069 {
5070     COLUMN_INFO *lpColumnInfo;
5071     RECT rcOld, rcCol;
5072     POINT ptOrigin;
5073     INT nCol;
5074     HDITEMW hdi;
5075
5076     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
5077     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
5078     rcCol = lpColumnInfo->rcHeader;
5079     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5080         rcCol.left = rcCol.right;
5081
5082     /* adjust the other columns */
5083     hdi.mask = HDI_ORDER;
5084     if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
5085     {
5086         INT nOrder = hdi.iOrder;
5087         for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
5088         {
5089             hdi.mask = HDI_ORDER;
5090             SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi);
5091             if (hdi.iOrder >= nOrder) {
5092                 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
5093                 lpColumnInfo->rcHeader.left  += dx;
5094                 lpColumnInfo->rcHeader.right += dx;
5095             }
5096         }
5097     }
5098
5099     /* do not update screen if not in report mode */
5100     if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
5101     
5102     /* Need to reset the item width when inserting a new column */
5103     infoPtr->nItemWidth += dx;
5104
5105     LISTVIEW_UpdateScroll(infoPtr);
5106     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
5107
5108     /* scroll to cover the deleted column, and invalidate for redraw */
5109     rcOld = infoPtr->rcList;
5110     rcOld.left = ptOrigin.x + rcCol.left + dx;
5111     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5112 }
5113
5114 /***
5115  * DESCRIPTION:
5116  * Removes a column from the listview control.
5117  *
5118  * PARAMETER(S):
5119  * [I] infoPtr : valid pointer to the listview structure
5120  * [I] nColumn : column index
5121  *
5122  * RETURN:
5123  *   SUCCESS : TRUE
5124  *   FAILURE : FALSE
5125  */
5126 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
5127 {
5128     RECT rcCol;
5129     
5130     TRACE("nColumn=%d\n", nColumn);
5131
5132     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
5133            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5134
5135     /* While the MSDN specifically says that column zero should not be deleted,
5136        what actually happens is that the column itself is deleted but no items or subitems
5137        are removed.
5138      */
5139
5140     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
5141     
5142     if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
5143         return FALSE;
5144
5145     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
5146     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
5147   
5148     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
5149     {
5150         SUBITEM_INFO *lpSubItem, *lpDelItem;
5151         HDPA hdpaSubItems;
5152         INT nItem, nSubItem, i;
5153         
5154         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5155         {
5156             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
5157             nSubItem = 0;
5158             lpDelItem = 0;
5159             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
5160             {
5161                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
5162                 if (lpSubItem->iSubItem == nColumn)
5163                 {
5164                     nSubItem = i;
5165                     lpDelItem = lpSubItem;
5166                 }
5167                 else if (lpSubItem->iSubItem > nColumn) 
5168                 {
5169                     lpSubItem->iSubItem--;
5170                 }
5171             }
5172
5173             /* if we found our subitem, zapp it */      
5174             if (nSubItem > 0)
5175             {
5176                 /* free string */
5177                 if (is_textW(lpDelItem->hdr.pszText))
5178                     Free(lpDelItem->hdr.pszText);
5179
5180                 /* free item */
5181                 Free(lpDelItem);
5182
5183                 /* free dpa memory */
5184                 DPA_DeletePtr(hdpaSubItems, nSubItem);
5185             }
5186         }
5187     }
5188
5189     /* update the other column info */
5190     LISTVIEW_UpdateItemSize(infoPtr);
5191     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
5192         LISTVIEW_InvalidateList(infoPtr);
5193     else
5194         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
5195
5196     return TRUE;
5197 }
5198
5199 /***
5200  * DESCRIPTION:
5201  * Invalidates the listview after an item's insertion or deletion.
5202  *
5203  * PARAMETER(S):
5204  * [I] infoPtr : valid pointer to the listview structure
5205  * [I] nItem : item index
5206  * [I] dir : -1 if deleting, 1 if inserting
5207  *
5208  * RETURN:
5209  *   None
5210  */
5211 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5212 {
5213     INT nPerCol, nItemCol, nItemRow;
5214     RECT rcScroll;
5215     POINT Origin;
5216
5217     /* if we don't refresh, what's the point of scrolling? */
5218     if (!is_redrawing(infoPtr)) return;
5219     
5220     assert (abs(dir) == 1);
5221
5222     /* arrange icons if autoarrange is on */
5223     if (is_autoarrange(infoPtr))
5224     {
5225         BOOL arrange = TRUE;
5226         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5227         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5228         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5229     }
5230
5231     /* scrollbars need updating */
5232     LISTVIEW_UpdateScroll(infoPtr);
5233
5234     /* figure out the item's position */ 
5235     if (infoPtr->uView == LV_VIEW_DETAILS)
5236         nPerCol = infoPtr->nItemCount + 1;
5237     else if (infoPtr->uView == LV_VIEW_LIST)
5238         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5239     else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5240         return;
5241     
5242     nItemCol = nItem / nPerCol;
5243     nItemRow = nItem % nPerCol;
5244     LISTVIEW_GetOrigin(infoPtr, &Origin);
5245
5246     /* move the items below up a slot */
5247     rcScroll.left = nItemCol * infoPtr->nItemWidth;
5248     rcScroll.top = nItemRow * infoPtr->nItemHeight;
5249     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5250     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5251     OffsetRect(&rcScroll, Origin.x, Origin.y);
5252     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5253     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5254     {
5255         TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5256         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
5257                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5258     }
5259
5260     /* report has only that column, so we're done */
5261     if (infoPtr->uView == LV_VIEW_DETAILS) return;
5262
5263     /* now for LISTs, we have to deal with the columns to the right */
5264     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
5265     rcScroll.top = 0;
5266     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
5267     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5268     OffsetRect(&rcScroll, Origin.x, Origin.y);
5269     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5270         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5271                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5272 }
5273
5274 /***
5275  * DESCRIPTION:
5276  * Removes an item from the listview control.
5277  *
5278  * PARAMETER(S):
5279  * [I] infoPtr : valid pointer to the listview structure
5280  * [I] nItem : item index
5281  *
5282  * RETURN:
5283  *   SUCCESS : TRUE
5284  *   FAILURE : FALSE
5285  */
5286 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5287 {
5288     LVITEMW item;
5289     const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5290
5291     TRACE("(nItem=%d)\n", nItem);
5292
5293     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5294     
5295     /* remove selection, and focus */
5296     item.state = 0;
5297     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5298     LISTVIEW_SetItemState(infoPtr, nItem, &item);
5299             
5300     /* send LVN_DELETEITEM notification. */
5301     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5302
5303     /* we need to do this here, because we'll be deleting stuff */  
5304     if (is_icon)
5305         LISTVIEW_InvalidateItem(infoPtr, nItem);
5306     
5307     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5308     {
5309         HDPA hdpaSubItems;
5310         ITEMHDR *hdrItem;
5311         ITEM_INFO *lpItem;
5312         ITEM_ID *lpID;
5313         INT i;
5314
5315         hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5316         lpItem = DPA_GetPtr(hdpaSubItems, 0);
5317
5318         /* free id struct */
5319         i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5320         lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5321         DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5322         Free(lpID);
5323         for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5324         {
5325             hdrItem = DPA_GetPtr(hdpaSubItems, i);
5326             if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5327             Free(hdrItem);
5328         }
5329         DPA_Destroy(hdpaSubItems);
5330     }
5331
5332     if (is_icon)
5333     {
5334         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5335         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5336     }
5337
5338     infoPtr->nItemCount--;
5339     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5340
5341     /* now is the invalidation fun */
5342     if (!is_icon)
5343         LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5344     return TRUE;
5345 }
5346
5347
5348 /***
5349  * DESCRIPTION:
5350  * Callback implementation for editlabel control
5351  *
5352  * PARAMETER(S):
5353  * [I] infoPtr : valid pointer to the listview structure
5354  * [I] storeText : store edit box text as item text
5355  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5356  *
5357  * RETURN:
5358  *   SUCCESS : TRUE
5359  *   FAILURE : FALSE
5360  */
5361 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5362 {
5363     HWND hwndSelf = infoPtr->hwndSelf;
5364     NMLVDISPINFOW dispInfo;
5365     INT editedItem = infoPtr->nEditLabelItem;
5366     BOOL bSame;
5367     WCHAR *pszText = NULL;
5368     BOOL res;
5369
5370     if (storeText)
5371     {
5372         DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
5373
5374         if (len)
5375         {
5376             if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5377             {
5378                 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
5379                 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
5380             }
5381         }
5382     }
5383
5384     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5385
5386     infoPtr->nEditLabelItem = -1;
5387     infoPtr->hwndEdit = 0;
5388
5389     ZeroMemory(&dispInfo, sizeof(dispInfo));
5390     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5391     dispInfo.item.iItem = editedItem;
5392     dispInfo.item.iSubItem = 0;
5393     dispInfo.item.stateMask = ~0;
5394     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item))
5395     {
5396        res = FALSE;
5397        goto cleanup;
5398     }
5399
5400     if (isW)
5401         bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5402     else
5403     {
5404         LPWSTR tmp = textdupTtoW(pszText, FALSE);
5405         bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5406         textfreeT(tmp, FALSE);
5407     }
5408     if (bSame)
5409     {
5410         res = TRUE;
5411         goto cleanup;
5412     }
5413
5414     /* add the text from the edit in */
5415     dispInfo.item.mask |= LVIF_TEXT;
5416     dispInfo.item.pszText = pszText;
5417     dispInfo.item.cchTextMax = textlenT(pszText, isW);
5418
5419     /* Do we need to update the Item Text */
5420     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
5421     {
5422         res = FALSE;
5423         goto cleanup;
5424     }
5425     if (!IsWindow(hwndSelf))
5426     {
5427         res = FALSE;
5428         goto cleanup;
5429     }
5430     if (!pszText) return TRUE;
5431
5432     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5433     {
5434         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5435         ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5436         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5437         {
5438             LISTVIEW_InvalidateItem(infoPtr, editedItem);
5439             res = TRUE;
5440             goto cleanup;
5441         }
5442     }
5443
5444     ZeroMemory(&dispInfo, sizeof(dispInfo));
5445     dispInfo.item.mask = LVIF_TEXT;
5446     dispInfo.item.iItem = editedItem;
5447     dispInfo.item.iSubItem = 0;
5448     dispInfo.item.pszText = pszText;
5449     dispInfo.item.cchTextMax = textlenT(pszText, isW);
5450     res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5451
5452 cleanup:
5453     Free(pszText);
5454
5455     return res;
5456 }
5457
5458 /***
5459  * DESCRIPTION:
5460  * Begin in place editing of specified list view item
5461  *
5462  * PARAMETER(S):
5463  * [I] infoPtr : valid pointer to the listview structure
5464  * [I] nItem : item index
5465  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5466  *
5467  * RETURN:
5468  *   SUCCESS : TRUE
5469  *   FAILURE : FALSE
5470  */
5471 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5472 {
5473     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5474     NMLVDISPINFOW dispInfo;
5475     RECT rect;
5476     SIZE sz;
5477     HWND hwndSelf = infoPtr->hwndSelf;
5478     HDC hdc;
5479     HFONT hOldFont = NULL;
5480     TEXTMETRICW textMetric;
5481
5482     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5483
5484     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5485
5486     /* Is the EditBox still there, if so remove it */
5487     if(infoPtr->hwndEdit != 0)
5488     {
5489         SetFocus(infoPtr->hwndSelf);
5490         infoPtr->hwndEdit = 0;
5491     }
5492
5493     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5494
5495     infoPtr->nEditLabelItem = nItem;
5496
5497     LISTVIEW_SetSelection(infoPtr, nItem);
5498     LISTVIEW_SetItemFocus(infoPtr, nItem);
5499     LISTVIEW_InvalidateItem(infoPtr, nItem);
5500
5501     rect.left = LVIR_LABEL;
5502     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5503     
5504     ZeroMemory(&dispInfo, sizeof(dispInfo));
5505     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5506     dispInfo.item.iItem = nItem;
5507     dispInfo.item.iSubItem = 0;
5508     dispInfo.item.stateMask = ~0;
5509     dispInfo.item.pszText = szDispText;
5510     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5511     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5512
5513     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, isW);
5514     if (!infoPtr->hwndEdit) return 0;
5515     
5516     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5517     {
5518         if (!IsWindow(hwndSelf))
5519             return 0;
5520         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5521         infoPtr->hwndEdit = 0;
5522         return 0;
5523     }
5524
5525     /* Now position and display edit box */
5526     hdc = GetDC(infoPtr->hwndSelf);
5527
5528     /* Select the font to get appropriate metric dimensions */
5529     if(infoPtr->hFont != 0)
5530         hOldFont = SelectObject(hdc, infoPtr->hFont);
5531
5532     /* Get String Length in pixels */
5533     GetTextExtentPoint32W(hdc, dispInfo.item.pszText, lstrlenW(dispInfo.item.pszText), &sz);
5534
5535     /* Add Extra spacing for the next character */
5536     GetTextMetricsW(hdc, &textMetric);
5537     sz.cx += (textMetric.tmMaxCharWidth * 2);
5538
5539     if(infoPtr->hFont != 0)
5540         SelectObject(hdc, hOldFont);
5541
5542     ReleaseDC(infoPtr->hwndSelf, hdc);
5543
5544     MoveWindow(infoPtr->hwndEdit, rect.left - 2, rect.top - 1, sz.cx,
5545                                   rect.bottom - rect.top + 2, FALSE);
5546     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5547     SetFocus(infoPtr->hwndEdit);
5548     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5549     return infoPtr->hwndEdit;
5550 }
5551
5552
5553 /***
5554  * DESCRIPTION:
5555  * Ensures the specified item is visible, scrolling into view if necessary.
5556  *
5557  * PARAMETER(S):
5558  * [I] infoPtr : valid pointer to the listview structure
5559  * [I] nItem : item index
5560  * [I] bPartial : partially or entirely visible
5561  *
5562  * RETURN:
5563  *   SUCCESS : TRUE
5564  *   FAILURE : FALSE
5565  */
5566 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5567 {
5568     INT nScrollPosHeight = 0;
5569     INT nScrollPosWidth = 0;
5570     INT nHorzAdjust = 0;
5571     INT nVertAdjust = 0;
5572     INT nHorzDiff = 0;
5573     INT nVertDiff = 0;
5574     RECT rcItem, rcTemp;
5575
5576     rcItem.left = LVIR_BOUNDS;
5577     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5578
5579     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5580     
5581     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5582     {
5583         /* scroll left/right, but in LV_VIEW_DETAILS mode */
5584         if (infoPtr->uView == LV_VIEW_LIST)
5585             nScrollPosWidth = infoPtr->nItemWidth;
5586         else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
5587             nScrollPosWidth = 1;
5588
5589         if (rcItem.left < infoPtr->rcList.left)
5590         {
5591             nHorzAdjust = -1;
5592             if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5593         }
5594         else
5595         {
5596             nHorzAdjust = 1;
5597             if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5598         }
5599     }
5600
5601     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5602     {
5603         /* scroll up/down, but not in LVS_LIST mode */
5604         if (infoPtr->uView == LV_VIEW_DETAILS)
5605             nScrollPosHeight = infoPtr->nItemHeight;
5606         else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
5607             nScrollPosHeight = 1;
5608
5609         if (rcItem.top < infoPtr->rcList.top)
5610         {
5611             nVertAdjust = -1;
5612             if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5613         }
5614         else
5615         {
5616             nVertAdjust = 1;
5617             if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5618         }
5619     }
5620
5621     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5622
5623     if (nScrollPosWidth)
5624     {
5625         INT diff = nHorzDiff / nScrollPosWidth;
5626         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5627         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5628     }
5629
5630     if (nScrollPosHeight)
5631     {
5632         INT diff = nVertDiff / nScrollPosHeight;
5633         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5634         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5635     }
5636
5637     return TRUE;
5638 }
5639
5640 /***
5641  * DESCRIPTION:
5642  * Searches for an item with specific characteristics.
5643  *
5644  * PARAMETER(S):
5645  * [I] hwnd : window handle
5646  * [I] nStart : base item index
5647  * [I] lpFindInfo : item information to look for
5648  *
5649  * RETURN:
5650  *   SUCCESS : index of item
5651  *   FAILURE : -1
5652  */
5653 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5654                               const LVFINDINFOW *lpFindInfo)
5655 {
5656     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5657     BOOL bWrap = FALSE, bNearest = FALSE;
5658     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5659     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5660     POINT Position, Destination;
5661     LVITEMW lvItem;
5662
5663     /* Search in virtual listviews should be done by application, not by
5664        listview control, so we just send LVN_ODFINDITEMW and return the result */
5665     if (infoPtr->dwStyle & LVS_OWNERDATA)
5666     {
5667         NMLVFINDITEMW nmlv;
5668
5669         nmlv.iStart = nStart;
5670         nmlv.lvfi = *lpFindInfo;
5671         return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5672     }
5673
5674     if (!lpFindInfo || nItem < 0) return -1;
5675     
5676     lvItem.mask = 0;
5677     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5678     {
5679         lvItem.mask |= LVIF_TEXT;
5680         lvItem.pszText = szDispText;
5681         lvItem.cchTextMax = DISP_TEXT_SIZE;
5682     }
5683
5684     if (lpFindInfo->flags & LVFI_WRAP)
5685         bWrap = TRUE;
5686
5687     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
5688         (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
5689     {
5690         POINT Origin;
5691         RECT rcArea;
5692         
5693         LISTVIEW_GetOrigin(infoPtr, &Origin);
5694         Destination.x = lpFindInfo->pt.x - Origin.x;
5695         Destination.y = lpFindInfo->pt.y - Origin.y;
5696         switch(lpFindInfo->vkDirection)
5697         {
5698         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
5699         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
5700         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5701         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
5702         case VK_HOME:  Destination.x = Destination.y = 0; break;
5703         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5704         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5705         case VK_END:
5706             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5707             Destination.x = rcArea.right; 
5708             Destination.y = rcArea.bottom; 
5709             break;
5710         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5711         }
5712         bNearest = TRUE;
5713     }
5714     else Destination.x = Destination.y = 0;
5715
5716     /* if LVFI_PARAM is specified, all other flags are ignored */
5717     if (lpFindInfo->flags & LVFI_PARAM)
5718     {
5719         lvItem.mask |= LVIF_PARAM;
5720         bNearest = FALSE;
5721         lvItem.mask &= ~LVIF_TEXT;
5722     }
5723
5724 again:
5725     for (; nItem < nLast; nItem++)
5726     {
5727         lvItem.iItem = nItem;
5728         lvItem.iSubItem = 0;
5729         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5730
5731         if (lvItem.mask & LVIF_PARAM)
5732         {
5733             if (lpFindInfo->lParam == lvItem.lParam)
5734                 return nItem;
5735             else
5736                 continue;
5737         }
5738         
5739         if (lvItem.mask & LVIF_TEXT)
5740         {
5741             if (lpFindInfo->flags & LVFI_PARTIAL)
5742             {
5743                 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5744             }
5745             else
5746             {
5747                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5748             }
5749         }
5750
5751         if (!bNearest) return nItem;
5752         
5753         /* This is very inefficient. To do a good job here,
5754          * we need a sorted array of (x,y) item positions */
5755         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5756
5757         /* compute the distance^2 to the destination */
5758         xdist = Destination.x - Position.x;
5759         ydist = Destination.y - Position.y;
5760         dist = xdist * xdist + ydist * ydist;
5761
5762         /* remember the distance, and item if it's closer */
5763         if (dist < mindist)
5764         {
5765             mindist = dist;
5766             nNearestItem = nItem;
5767         }
5768     }
5769
5770     if (bWrap)
5771     {
5772         nItem = 0;
5773         nLast = min(nStart + 1, infoPtr->nItemCount);
5774         bWrap = FALSE;
5775         goto again;
5776     }
5777
5778     return nNearestItem;
5779 }
5780
5781 /***
5782  * DESCRIPTION:
5783  * Searches for an item with specific characteristics.
5784  *
5785  * PARAMETER(S):
5786  * [I] hwnd : window handle
5787  * [I] nStart : base item index
5788  * [I] lpFindInfo : item information to look for
5789  *
5790  * RETURN:
5791  *   SUCCESS : index of item
5792  *   FAILURE : -1
5793  */
5794 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5795                               const LVFINDINFOA *lpFindInfo)
5796 {
5797     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5798     LVFINDINFOW fiw;
5799     INT res;
5800     LPWSTR strW = NULL;
5801
5802     memcpy(&fiw, lpFindInfo, sizeof(fiw));
5803     if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5804     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5805     textfreeT(strW, FALSE);
5806     return res;
5807 }
5808
5809 /***
5810  * DESCRIPTION:
5811  * Retrieves the background image of the listview control.
5812  *
5813  * PARAMETER(S):
5814  * [I] infoPtr : valid pointer to the listview structure
5815  * [O] lpBkImage : background image attributes
5816  *
5817  * RETURN:
5818  *   SUCCESS : TRUE
5819  *   FAILURE : FALSE
5820  */
5821 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
5822 /* {   */
5823 /*   FIXME (listview, "empty stub!\n"); */
5824 /*   return FALSE;   */
5825 /* }   */
5826
5827 /***
5828  * DESCRIPTION:
5829  * Retrieves column attributes.
5830  *
5831  * PARAMETER(S):
5832  * [I] infoPtr : valid pointer to the listview structure
5833  * [I] nColumn :  column index
5834  * [IO] lpColumn : column information
5835  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5836  *           otherwise it is in fact a LPLVCOLUMNA
5837  *
5838  * RETURN:
5839  *   SUCCESS : TRUE
5840  *   FAILURE : FALSE
5841  */
5842 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5843 {
5844     COLUMN_INFO *lpColumnInfo;
5845     HDITEMW hdi;
5846
5847     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5848     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5849
5850     /* initialize memory */
5851     ZeroMemory(&hdi, sizeof(hdi));
5852
5853     if (lpColumn->mask & LVCF_TEXT)
5854     {
5855         hdi.mask |= HDI_TEXT;
5856         hdi.pszText = lpColumn->pszText;
5857         hdi.cchTextMax = lpColumn->cchTextMax;
5858     }
5859
5860     if (lpColumn->mask & LVCF_IMAGE)
5861         hdi.mask |= HDI_IMAGE;
5862
5863     if (lpColumn->mask & LVCF_ORDER)
5864         hdi.mask |= HDI_ORDER;
5865
5866     if (lpColumn->mask & LVCF_SUBITEM)
5867         hdi.mask |= HDI_LPARAM;
5868
5869     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5870
5871     if (lpColumn->mask & LVCF_FMT)
5872         lpColumn->fmt = lpColumnInfo->fmt;
5873
5874     if (lpColumn->mask & LVCF_WIDTH)
5875         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5876
5877     if (lpColumn->mask & LVCF_IMAGE)
5878         lpColumn->iImage = hdi.iImage;
5879
5880     if (lpColumn->mask & LVCF_ORDER)
5881         lpColumn->iOrder = hdi.iOrder;
5882
5883     if (lpColumn->mask & LVCF_SUBITEM)
5884         lpColumn->iSubItem = hdi.lParam;
5885
5886     if (lpColumn->mask & LVCF_MINWIDTH)
5887         lpColumn->cxMin = lpColumnInfo->cxMin;
5888
5889     return TRUE;
5890 }
5891
5892
5893 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5894 {
5895     TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5896
5897     if (!lpiArray)
5898         return FALSE;
5899
5900     return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5901 }
5902
5903 /***
5904  * DESCRIPTION:
5905  * Retrieves the column width.
5906  *
5907  * PARAMETER(S):
5908  * [I] infoPtr : valid pointer to the listview structure
5909  * [I] int : column index
5910  *
5911  * RETURN:
5912  *   SUCCESS : column width
5913  *   FAILURE : zero
5914  */
5915 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5916 {
5917     INT nColumnWidth = 0;
5918     HDITEMW hdItem;
5919
5920     TRACE("nColumn=%d\n", nColumn);
5921
5922     /* we have a 'column' in LIST and REPORT mode only */
5923     switch(infoPtr->uView)
5924     {
5925     case LV_VIEW_LIST:
5926         nColumnWidth = infoPtr->nItemWidth;
5927         break;
5928     case LV_VIEW_DETAILS:
5929         /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
5930          * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5931          * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5932          *
5933          * TODO: should we do the same in LVM_GETCOLUMN?
5934          */
5935         hdItem.mask = HDI_WIDTH;
5936         if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5937         {
5938             WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5939             return 0;
5940         }
5941         nColumnWidth = hdItem.cxy;
5942         break;
5943     }
5944
5945     TRACE("nColumnWidth=%d\n", nColumnWidth);
5946     return nColumnWidth;
5947 }
5948
5949 /***
5950  * DESCRIPTION:
5951  * In list or report display mode, retrieves the number of items that can fit
5952  * vertically in the visible area. In icon or small icon display mode,
5953  * retrieves the total number of visible items.
5954  *
5955  * PARAMETER(S):
5956  * [I] infoPtr : valid pointer to the listview structure
5957  *
5958  * RETURN:
5959  * Number of fully visible items.
5960  */
5961 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5962 {
5963     switch (infoPtr->uView)
5964     {
5965     case LV_VIEW_ICON:
5966     case LV_VIEW_SMALLICON:
5967         return infoPtr->nItemCount;
5968     case LV_VIEW_DETAILS:
5969         return LISTVIEW_GetCountPerColumn(infoPtr);
5970     case LV_VIEW_LIST:
5971         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5972     }
5973     assert(FALSE);
5974     return 0;
5975 }
5976
5977 /***
5978  * DESCRIPTION:
5979  * Retrieves an image list handle.
5980  *
5981  * PARAMETER(S):
5982  * [I] infoPtr : valid pointer to the listview structure
5983  * [I] nImageList : image list identifier
5984  *
5985  * RETURN:
5986  *   SUCCESS : image list handle
5987  *   FAILURE : NULL
5988  */
5989 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5990 {
5991     switch (nImageList)
5992     {
5993     case LVSIL_NORMAL: return infoPtr->himlNormal;
5994     case LVSIL_SMALL: return infoPtr->himlSmall;
5995     case LVSIL_STATE: return infoPtr->himlState;
5996     }
5997     return NULL;
5998 }
5999
6000 /* LISTVIEW_GetISearchString */
6001
6002 /***
6003  * DESCRIPTION:
6004  * Retrieves item attributes.
6005  *
6006  * PARAMETER(S):
6007  * [I] hwnd : window handle
6008  * [IO] lpLVItem : item info
6009  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6010  *           if FALSE, then lpLVItem is a LPLVITEMA.
6011  *
6012  * NOTE:
6013  *   This is the internal 'GetItem' interface -- it tries to
6014  *   be smart and avoid text copies, if possible, by modifying
6015  *   lpLVItem->pszText to point to the text string. Please note
6016  *   that this is not always possible (e.g. OWNERDATA), so on
6017  *   entry you *must* supply valid values for pszText, and cchTextMax.
6018  *   The only difference to the documented interface is that upon
6019  *   return, you should use *only* the lpLVItem->pszText, rather than
6020  *   the buffer pointer you provided on input. Most code already does
6021  *   that, so it's not a problem.
6022  *   For the two cases when the text must be copied (that is,
6023  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6024  *
6025  * RETURN:
6026  *   SUCCESS : TRUE
6027  *   FAILURE : FALSE
6028  */
6029 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6030 {
6031     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
6032     NMLVDISPINFOW dispInfo;
6033     ITEM_INFO *lpItem;
6034     ITEMHDR* pItemHdr;
6035     HDPA hdpaSubItems;
6036     INT isubitem;
6037
6038     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6039
6040     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6041         return FALSE;
6042
6043     if (lpLVItem->mask == 0) return TRUE;
6044
6045     /* make a local copy */
6046     isubitem = lpLVItem->iSubItem;
6047
6048     /* a quick optimization if all we're asked is the focus state
6049      * these queries are worth optimising since they are common,
6050      * and can be answered in constant time, without the heavy accesses */
6051     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
6052          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
6053     {
6054         lpLVItem->state = 0;
6055         if (infoPtr->nFocusedItem == lpLVItem->iItem)
6056             lpLVItem->state |= LVIS_FOCUSED;
6057         return TRUE;
6058     }
6059
6060     ZeroMemory(&dispInfo, sizeof(dispInfo));
6061
6062     /* if the app stores all the data, handle it separately */
6063     if (infoPtr->dwStyle & LVS_OWNERDATA)
6064     {
6065         dispInfo.item.state = 0;
6066
6067         /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6068         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
6069            ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
6070         {
6071             UINT mask = lpLVItem->mask;
6072
6073             /* NOTE: copy only fields which we _know_ are initialized, some apps
6074              *       depend on the uninitialized fields being 0 */
6075             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
6076             dispInfo.item.iItem = lpLVItem->iItem;
6077             dispInfo.item.iSubItem = isubitem;
6078             if (lpLVItem->mask & LVIF_TEXT)
6079             {
6080                 if (lpLVItem->mask & LVIF_NORECOMPUTE)
6081                     /* reset mask */
6082                     dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
6083                 else
6084                 {
6085                     dispInfo.item.pszText = lpLVItem->pszText;
6086                     dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6087                 }
6088             }
6089             if (lpLVItem->mask & LVIF_STATE)
6090                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
6091             /* could be zeroed on LVIF_NORECOMPUTE case */
6092             if (dispInfo.item.mask != 0)
6093             {
6094                 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6095                 dispInfo.item.stateMask = lpLVItem->stateMask;
6096                 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6097                 {
6098                     /* full size structure expected - _WIN32IE >= 0x560 */
6099                     *lpLVItem = dispInfo.item;
6100                 }
6101                 else if (lpLVItem->mask & LVIF_INDENT)
6102                 {
6103                     /* indent member expected - _WIN32IE >= 0x300 */
6104                     memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
6105                 }
6106                 else
6107                 {
6108                     /* minimal structure expected */
6109                     memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
6110                 }
6111                 lpLVItem->mask = mask;
6112                 TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
6113             }
6114         }
6115         
6116         /* make sure lParam is zeroed out */
6117         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
6118
6119         /* callback marked pointer required here */
6120         if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
6121             lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
6122
6123         /* we store only a little state, so if we're not asked, we're done */
6124         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
6125
6126         /* if focus is handled by us, report it */
6127         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
6128         {
6129             lpLVItem->state &= ~LVIS_FOCUSED;
6130             if (infoPtr->nFocusedItem == lpLVItem->iItem)
6131                 lpLVItem->state |= LVIS_FOCUSED;
6132         }
6133
6134         /* and do the same for selection, if we handle it */
6135         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
6136         {
6137             lpLVItem->state &= ~LVIS_SELECTED;
6138             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6139                 lpLVItem->state |= LVIS_SELECTED;
6140         }
6141         
6142         return TRUE;
6143     }
6144
6145     /* find the item and subitem structures before we proceed */
6146     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
6147     lpItem = DPA_GetPtr(hdpaSubItems, 0);
6148     assert (lpItem);
6149
6150     if (isubitem)
6151     {
6152         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
6153         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
6154         if (!lpSubItem)
6155         {
6156             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
6157             isubitem = 0;
6158         }
6159     }
6160     else
6161         pItemHdr = &lpItem->hdr;
6162
6163     /* Do we need to query the state from the app? */
6164     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
6165     {
6166         dispInfo.item.mask |= LVIF_STATE;
6167         dispInfo.item.stateMask = infoPtr->uCallbackMask;
6168     }
6169   
6170     /* Do we need to enquire about the image? */
6171     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
6172         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
6173     {
6174         dispInfo.item.mask |= LVIF_IMAGE;
6175         dispInfo.item.iImage = I_IMAGECALLBACK;
6176     }
6177
6178     /* Only items support indentation */
6179     if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
6180         (isubitem == 0))
6181     {
6182         dispInfo.item.mask |= LVIF_INDENT;
6183         dispInfo.item.iIndent = I_INDENTCALLBACK;
6184     }
6185
6186     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6187     if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
6188         !is_textW(pItemHdr->pszText))
6189     {
6190         dispInfo.item.mask |= LVIF_TEXT;
6191         dispInfo.item.pszText = lpLVItem->pszText;
6192         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6193         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
6194             *dispInfo.item.pszText = '\0';
6195     }
6196
6197     /* If we don't have all the requested info, query the application */
6198     if (dispInfo.item.mask != 0)
6199     {
6200         dispInfo.item.iItem = lpLVItem->iItem;
6201         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
6202         dispInfo.item.lParam = lpItem->lParam;
6203         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6204         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
6205     }
6206
6207     /* we should not store values for subitems */
6208     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
6209
6210     /* Now, handle the iImage field */
6211     if (dispInfo.item.mask & LVIF_IMAGE)
6212     {
6213         lpLVItem->iImage = dispInfo.item.iImage;
6214         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6215             pItemHdr->iImage = dispInfo.item.iImage;
6216     }
6217     else if (lpLVItem->mask & LVIF_IMAGE)
6218     {
6219         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6220             lpLVItem->iImage = pItemHdr->iImage;
6221         else
6222             lpLVItem->iImage = 0;
6223     }
6224
6225     /* The pszText field */
6226     if (dispInfo.item.mask & LVIF_TEXT)
6227     {
6228         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6229             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6230
6231         lpLVItem->pszText = dispInfo.item.pszText;
6232     }
6233     else if (lpLVItem->mask & LVIF_TEXT)
6234     {
6235         /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6236         if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
6237         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
6238     }
6239
6240     /* Next is the lParam field */
6241     if (dispInfo.item.mask & LVIF_PARAM)
6242     {
6243         lpLVItem->lParam = dispInfo.item.lParam;
6244         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
6245             lpItem->lParam = dispInfo.item.lParam;
6246     }
6247     else if (lpLVItem->mask & LVIF_PARAM)
6248         lpLVItem->lParam = lpItem->lParam;
6249
6250     /* if this is a subitem, we're done */
6251     if (isubitem) return TRUE;
6252
6253     /* ... the state field (this one is different due to uCallbackmask) */
6254     if (lpLVItem->mask & LVIF_STATE)
6255     {
6256         lpLVItem->state = lpItem->state & lpLVItem->stateMask;
6257         if (dispInfo.item.mask & LVIF_STATE)
6258         {
6259             lpLVItem->state &= ~dispInfo.item.stateMask;
6260             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
6261         }
6262         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
6263         {
6264             lpLVItem->state &= ~LVIS_FOCUSED;
6265             if (infoPtr->nFocusedItem == lpLVItem->iItem)
6266                 lpLVItem->state |= LVIS_FOCUSED;
6267         }
6268         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
6269         {
6270             lpLVItem->state &= ~LVIS_SELECTED;
6271             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6272                 lpLVItem->state |= LVIS_SELECTED;
6273         }           
6274     }
6275
6276     /* and last, but not least, the indent field */
6277     if (dispInfo.item.mask & LVIF_INDENT)
6278     {
6279         lpLVItem->iIndent = dispInfo.item.iIndent;
6280         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
6281             lpItem->iIndent = dispInfo.item.iIndent;
6282     }
6283     else if (lpLVItem->mask & LVIF_INDENT)
6284     {
6285         lpLVItem->iIndent = lpItem->iIndent;
6286     }
6287
6288     return TRUE;
6289 }
6290
6291 /***
6292  * DESCRIPTION:
6293  * Retrieves item attributes.
6294  *
6295  * PARAMETER(S):
6296  * [I] hwnd : window handle
6297  * [IO] lpLVItem : item info
6298  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6299  *           if FALSE, then lpLVItem is a LPLVITEMA.
6300  *
6301  * NOTE:
6302  *   This is the external 'GetItem' interface -- it properly copies
6303  *   the text in the provided buffer.
6304  *
6305  * RETURN:
6306  *   SUCCESS : TRUE
6307  *   FAILURE : FALSE
6308  */
6309 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6310 {
6311     LPWSTR pszText;
6312     BOOL bResult;
6313
6314     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6315         return FALSE;
6316
6317     pszText = lpLVItem->pszText;
6318     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
6319     if (bResult && lpLVItem->pszText != pszText)
6320     {
6321         if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6322             textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
6323         else
6324             pszText = LPSTR_TEXTCALLBACKW;
6325     }
6326     lpLVItem->pszText = pszText;
6327
6328     return bResult;
6329 }
6330
6331
6332 /***
6333  * DESCRIPTION:
6334  * Retrieves the position (upper-left) of the listview control item.
6335  * Note that for LVS_ICON style, the upper-left is that of the icon
6336  * and not the bounding box.
6337  *
6338  * PARAMETER(S):
6339  * [I] infoPtr : valid pointer to the listview structure
6340  * [I] nItem : item index
6341  * [O] lpptPosition : coordinate information
6342  *
6343  * RETURN:
6344  *   SUCCESS : TRUE
6345  *   FAILURE : FALSE
6346  */
6347 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
6348 {
6349     POINT Origin;
6350
6351     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6352
6353     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6354
6355     LISTVIEW_GetOrigin(infoPtr, &Origin);
6356     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6357
6358     if (infoPtr->uView == LV_VIEW_ICON)
6359     {
6360         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6361         lpptPosition->y += ICON_TOP_PADDING;
6362     }
6363     lpptPosition->x += Origin.x;
6364     lpptPosition->y += Origin.y;
6365     
6366     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
6367     return TRUE;
6368 }
6369
6370
6371 /***
6372  * DESCRIPTION:
6373  * Retrieves the bounding rectangle for a listview control item.
6374  *
6375  * PARAMETER(S):
6376  * [I] infoPtr : valid pointer to the listview structure
6377  * [I] nItem : item index
6378  * [IO] lprc : bounding rectangle coordinates
6379  *     lprc->left specifies the portion of the item for which the bounding
6380  *     rectangle will be retrieved.
6381  *
6382  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6383  *        including the icon and label.
6384  *         *
6385  *         * For LVS_ICON
6386  *         * Experiment shows that native control returns:
6387  *         *  width = min (48, length of text line)
6388  *         *    .left = position.x - (width - iconsize.cx)/2
6389  *         *    .right = .left + width
6390  *         *  height = #lines of text * ntmHeight + icon height + 8
6391  *         *    .top = position.y - 2
6392  *         *    .bottom = .top + height
6393  *         *  separation between items .y = itemSpacing.cy - height
6394  *         *                           .x = itemSpacing.cx - width
6395  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
6396  *         *
6397  *         * For LVS_ICON
6398  *         * Experiment shows that native control returns:
6399  *         *  width = iconSize.cx + 16
6400  *         *    .left = position.x - (width - iconsize.cx)/2
6401  *         *    .right = .left + width
6402  *         *  height = iconSize.cy + 4
6403  *         *    .top = position.y - 2
6404  *         *    .bottom = .top + height
6405  *         *  separation between items .y = itemSpacing.cy - height
6406  *         *                           .x = itemSpacing.cx - width
6407  *     LVIR_LABEL Returns the bounding rectangle of the item text.
6408  *         *
6409  *         * For LVS_ICON
6410  *         * Experiment shows that native control returns:
6411  *         *  width = text length
6412  *         *    .left = position.x - width/2
6413  *         *    .right = .left + width
6414  *         *  height = ntmH * linecount + 2
6415  *         *    .top = position.y + iconSize.cy + 6
6416  *         *    .bottom = .top + height
6417  *         *  separation between items .y = itemSpacing.cy - height
6418  *         *                           .x = itemSpacing.cx - width
6419  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6420  *      rectangles, but excludes columns in report view.
6421  *
6422  * RETURN:
6423  *   SUCCESS : TRUE
6424  *   FAILURE : FALSE
6425  *
6426  * NOTES
6427  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
6428  *   upon whether the window has the focus currently and on whether the item
6429  *   is the one with the focus.  Ensure that the control's record of which
6430  *   item has the focus agrees with the items' records.
6431  */
6432 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6433 {
6434     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6435     BOOL doLabel = TRUE, oversizedBox = FALSE;
6436     POINT Position, Origin;
6437     LVITEMW lvItem;
6438     LONG mode;
6439
6440     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6441
6442     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6443
6444     LISTVIEW_GetOrigin(infoPtr, &Origin);
6445     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6446
6447     /* Be smart and try to figure out the minimum we have to do */
6448     if (lprc->left == LVIR_ICON) doLabel = FALSE;
6449     if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6450     if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6451         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6452         oversizedBox = TRUE;
6453
6454     /* get what we need from the item before hand, so we make
6455      * only one request. This can speed up things, if data
6456      * is stored on the app side */
6457     lvItem.mask = 0;
6458     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6459     if (doLabel) lvItem.mask |= LVIF_TEXT;
6460     lvItem.iItem = nItem;
6461     lvItem.iSubItem = 0;
6462     lvItem.pszText = szDispText;
6463     lvItem.cchTextMax = DISP_TEXT_SIZE;
6464     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6465     /* we got the state already up, simulate it here, to avoid a reget */
6466     if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6467     {
6468         lvItem.mask |= LVIF_STATE;
6469         lvItem.stateMask = LVIS_FOCUSED;
6470         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6471     }
6472
6473     if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6474         lprc->left = LVIR_BOUNDS;
6475
6476     mode = lprc->left;
6477     switch(lprc->left)
6478     {
6479     case LVIR_ICON:
6480         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6481         break;
6482
6483     case LVIR_LABEL:
6484         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6485         break;
6486
6487     case LVIR_BOUNDS:
6488         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6489         break;
6490
6491     case LVIR_SELECTBOUNDS:
6492         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6493         break;
6494
6495     default:
6496         WARN("Unknown value: %d\n", lprc->left);
6497         return FALSE;
6498     }
6499
6500     if (infoPtr->uView == LV_VIEW_DETAILS)
6501     {
6502         if (mode != LVIR_BOUNDS)
6503             OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left,
6504                              Position.y + Origin.y);
6505         else
6506             OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6507     }
6508     else
6509         OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6510
6511     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6512
6513     return TRUE;
6514 }
6515
6516 /***
6517  * DESCRIPTION:
6518  * Retrieves the spacing between listview control items.
6519  *
6520  * PARAMETER(S):
6521  * [I] infoPtr : valid pointer to the listview structure
6522  * [IO] lprc : rectangle to receive the output
6523  *             on input, lprc->top = nSubItem
6524  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6525  * 
6526  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6527  *       not only those of the first column.
6528  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6529  * 
6530  * RETURN:
6531  *     TRUE: success
6532  *     FALSE: failure
6533  */
6534 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6535 {
6536     POINT Position;
6537     LVITEMW lvItem;
6538     INT nColumn;
6539     
6540     if (!lprc) return FALSE;
6541
6542     nColumn = lprc->top;
6543
6544     TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6545     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6546     if (lprc->top == 0)
6547         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6548
6549     if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6550
6551     /* special case for header items */
6552     if (nItem == -1)
6553     {
6554         if (lprc->left != LVIR_BOUNDS)
6555         {
6556             FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6557             return FALSE;
6558         }
6559
6560         if (infoPtr->hwndHeader)
6561             return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6562         else
6563         {
6564             memset(lprc, 0, sizeof(RECT));
6565             return TRUE;
6566         }
6567     }
6568
6569     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6570
6571     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6572
6573     lvItem.mask = 0;
6574     lvItem.iItem = nItem;
6575     lvItem.iSubItem = nColumn;
6576     
6577     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6578     switch(lprc->left)
6579     {
6580     case LVIR_ICON:
6581         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6582         break;
6583
6584     case LVIR_LABEL:
6585     case LVIR_BOUNDS:
6586         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6587         break;
6588
6589     default:
6590         ERR("Unknown bounds=%d\n", lprc->left);
6591         return FALSE;
6592     }
6593
6594     OffsetRect(lprc, 0, Position.y);
6595     return TRUE;
6596 }
6597
6598
6599 /***
6600  * DESCRIPTION:
6601  * Retrieves the width of a label.
6602  *
6603  * PARAMETER(S):
6604  * [I] infoPtr : valid pointer to the listview structure
6605  *
6606  * RETURN:
6607  *   SUCCESS : string width (in pixels)
6608  *   FAILURE : zero
6609  */
6610 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6611 {
6612     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6613     LVITEMW lvItem;
6614
6615     TRACE("(nItem=%d)\n", nItem);
6616
6617     lvItem.mask = LVIF_TEXT;
6618     lvItem.iItem = nItem;
6619     lvItem.iSubItem = 0;
6620     lvItem.pszText = szDispText;
6621     lvItem.cchTextMax = DISP_TEXT_SIZE;
6622     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6623   
6624     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6625 }
6626
6627 /***
6628  * DESCRIPTION:
6629  * Retrieves the spacing between listview control items.
6630  *
6631  * PARAMETER(S):
6632  * [I] infoPtr : valid pointer to the listview structure
6633  * [I] bSmall : flag for small or large icon
6634  *
6635  * RETURN:
6636  * Horizontal + vertical spacing
6637  */
6638 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6639 {
6640   LONG lResult;
6641
6642   if (!bSmall)
6643   {
6644     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6645   }
6646   else
6647   {
6648     if (infoPtr->uView == LV_VIEW_ICON)
6649       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6650     else
6651       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6652   }
6653   return lResult;
6654 }
6655
6656 /***
6657  * DESCRIPTION:
6658  * Retrieves the state of a listview control item.
6659  *
6660  * PARAMETER(S):
6661  * [I] infoPtr : valid pointer to the listview structure
6662  * [I] nItem : item index
6663  * [I] uMask : state mask
6664  *
6665  * RETURN:
6666  * State specified by the mask.
6667  */
6668 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6669 {
6670     LVITEMW lvItem;
6671
6672     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6673
6674     lvItem.iItem = nItem;
6675     lvItem.iSubItem = 0;
6676     lvItem.mask = LVIF_STATE;
6677     lvItem.stateMask = uMask;
6678     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6679
6680     return lvItem.state & uMask;
6681 }
6682
6683 /***
6684  * DESCRIPTION:
6685  * Retrieves the text of a listview control item or subitem.
6686  *
6687  * PARAMETER(S):
6688  * [I] hwnd : window handle
6689  * [I] nItem : item index
6690  * [IO] lpLVItem : item information
6691  * [I] isW :  TRUE if lpLVItem is Unicode
6692  *
6693  * RETURN:
6694  *   SUCCESS : string length
6695  *   FAILURE : 0
6696  */
6697 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6698 {
6699     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6700
6701     lpLVItem->mask = LVIF_TEXT;
6702     lpLVItem->iItem = nItem;
6703     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6704
6705     return textlenT(lpLVItem->pszText, isW);
6706 }
6707
6708 /***
6709  * DESCRIPTION:
6710  * Searches for an item based on properties + relationships.
6711  *
6712  * PARAMETER(S):
6713  * [I] infoPtr : valid pointer to the listview structure
6714  * [I] nItem : item index
6715  * [I] uFlags : relationship flag
6716  *
6717  * RETURN:
6718  *   SUCCESS : item index
6719  *   FAILURE : -1
6720  */
6721 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6722 {
6723     UINT uMask = 0;
6724     LVFINDINFOW lvFindInfo;
6725     INT nCountPerColumn;
6726     INT nCountPerRow;
6727     INT i;
6728
6729     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6730     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6731
6732     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6733
6734     if (uFlags & LVNI_CUT)
6735       uMask |= LVIS_CUT;
6736
6737     if (uFlags & LVNI_DROPHILITED)
6738       uMask |= LVIS_DROPHILITED;
6739
6740     if (uFlags & LVNI_FOCUSED)
6741       uMask |= LVIS_FOCUSED;
6742
6743     if (uFlags & LVNI_SELECTED)
6744       uMask |= LVIS_SELECTED;
6745
6746     /* if we're asked for the focused item, that's only one, 
6747      * so it's worth optimizing */
6748     if (uFlags & LVNI_FOCUSED)
6749     {
6750         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6751         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6752     }
6753     
6754     if (uFlags & LVNI_ABOVE)
6755     {
6756       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6757       {
6758         while (nItem >= 0)
6759         {
6760           nItem--;
6761           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6762             return nItem;
6763         }
6764       }
6765       else
6766       {
6767         /* Special case for autoarrange - move 'til the top of a list */
6768         if (is_autoarrange(infoPtr))
6769         {
6770           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6771           while (nItem - nCountPerRow >= 0)
6772           {
6773             nItem -= nCountPerRow;
6774             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6775               return nItem;
6776           }
6777           return -1;
6778         }
6779         lvFindInfo.flags = LVFI_NEARESTXY;
6780         lvFindInfo.vkDirection = VK_UP;
6781         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6782         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6783         {
6784           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6785             return nItem;
6786         }
6787       }
6788     }
6789     else if (uFlags & LVNI_BELOW)
6790     {
6791       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6792       {
6793         while (nItem < infoPtr->nItemCount)
6794         {
6795           nItem++;
6796           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6797             return nItem;
6798         }
6799       }
6800       else
6801       {
6802         /* Special case for autoarrange - move 'til the bottom of a list */
6803         if (is_autoarrange(infoPtr))
6804         {
6805           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6806           while (nItem + nCountPerRow < infoPtr->nItemCount )
6807           {
6808             nItem += nCountPerRow;
6809             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6810               return nItem;
6811           }
6812           return -1;
6813         }
6814         lvFindInfo.flags = LVFI_NEARESTXY;
6815         lvFindInfo.vkDirection = VK_DOWN;
6816         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6817         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6818         {
6819           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6820             return nItem;
6821         }
6822       }
6823     }
6824     else if (uFlags & LVNI_TOLEFT)
6825     {
6826       if (infoPtr->uView == LV_VIEW_LIST)
6827       {
6828         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6829         while (nItem - nCountPerColumn >= 0)
6830         {
6831           nItem -= nCountPerColumn;
6832           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6833             return nItem;
6834         }
6835       }
6836       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6837       {
6838         /* Special case for autoarrange - move 'til the beginning of a row */
6839         if (is_autoarrange(infoPtr))
6840         {
6841           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6842           while (nItem % nCountPerRow > 0)
6843           {
6844             nItem --;
6845             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6846               return nItem;
6847           }
6848           return -1;
6849         }
6850         lvFindInfo.flags = LVFI_NEARESTXY;
6851         lvFindInfo.vkDirection = VK_LEFT;
6852         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6853         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6854         {
6855           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6856             return nItem;
6857         }
6858       }
6859     }
6860     else if (uFlags & LVNI_TORIGHT)
6861     {
6862       if (infoPtr->uView == LV_VIEW_LIST)
6863       {
6864         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6865         while (nItem + nCountPerColumn < infoPtr->nItemCount)
6866         {
6867           nItem += nCountPerColumn;
6868           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6869             return nItem;
6870         }
6871       }
6872       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6873       {
6874         /* Special case for autoarrange - move 'til the end of a row */
6875         if (is_autoarrange(infoPtr))
6876         {
6877           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6878           while (nItem % nCountPerRow < nCountPerRow - 1 )
6879           {
6880             nItem ++;
6881             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6882               return nItem;
6883           }
6884           return -1;
6885         }
6886         lvFindInfo.flags = LVFI_NEARESTXY;
6887         lvFindInfo.vkDirection = VK_RIGHT;
6888         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6889         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6890         {
6891           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6892             return nItem;
6893         }
6894       }
6895     }
6896     else
6897     {
6898       nItem++;
6899
6900       /* search by index */
6901       for (i = nItem; i < infoPtr->nItemCount; i++)
6902       {
6903         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6904           return i;
6905       }
6906     }
6907
6908     return -1;
6909 }
6910
6911 /* LISTVIEW_GetNumberOfWorkAreas */
6912
6913 /***
6914  * DESCRIPTION:
6915  * Retrieves the origin coordinates when in icon or small icon display mode.
6916  *
6917  * PARAMETER(S):
6918  * [I] infoPtr : valid pointer to the listview structure
6919  * [O] lpptOrigin : coordinate information
6920  *
6921  * RETURN:
6922  *   None.
6923  */
6924 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6925 {
6926     INT nHorzPos = 0, nVertPos = 0;
6927     SCROLLINFO scrollInfo;
6928
6929     scrollInfo.cbSize = sizeof(SCROLLINFO);    
6930     scrollInfo.fMask = SIF_POS;
6931     
6932     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6933         nHorzPos = scrollInfo.nPos;
6934     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6935         nVertPos = scrollInfo.nPos;
6936
6937     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6938
6939     lpptOrigin->x = infoPtr->rcList.left;
6940     lpptOrigin->y = infoPtr->rcList.top;
6941     if (infoPtr->uView == LV_VIEW_LIST)
6942         nHorzPos *= infoPtr->nItemWidth;
6943     else if (infoPtr->uView == LV_VIEW_DETAILS)
6944         nVertPos *= infoPtr->nItemHeight;
6945     
6946     lpptOrigin->x -= nHorzPos;
6947     lpptOrigin->y -= nVertPos;
6948
6949     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6950 }
6951
6952 /***
6953  * DESCRIPTION:
6954  * Retrieves the width of a string.
6955  *
6956  * PARAMETER(S):
6957  * [I] hwnd : window handle
6958  * [I] lpszText : text string to process
6959  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6960  *
6961  * RETURN:
6962  *   SUCCESS : string width (in pixels)
6963  *   FAILURE : zero
6964  */
6965 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6966 {
6967     SIZE stringSize;
6968     
6969     stringSize.cx = 0;    
6970     if (is_textT(lpszText, isW))
6971     {
6972         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6973         HDC hdc = GetDC(infoPtr->hwndSelf);
6974         HFONT hOldFont = SelectObject(hdc, hFont);
6975
6976         if (isW)
6977             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6978         else
6979             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6980         SelectObject(hdc, hOldFont);
6981         ReleaseDC(infoPtr->hwndSelf, hdc);
6982     }
6983     return stringSize.cx;
6984 }
6985
6986 /***
6987  * DESCRIPTION:
6988  * Determines which listview item is located at the specified position.
6989  *
6990  * PARAMETER(S):
6991  * [I] infoPtr : valid pointer to the listview structure
6992  * [IO] lpht : hit test information
6993  * [I] subitem : fill out iSubItem.
6994  * [I] select : return the index only if the hit selects the item
6995  *
6996  * NOTE:
6997  * (mm 20001022): We must not allow iSubItem to be touched, for
6998  * an app might pass only a structure with space up to iItem!
6999  * (MS Office 97 does that for instance in the file open dialog)
7000  * 
7001  * RETURN:
7002  *   SUCCESS : item index
7003  *   FAILURE : -1
7004  */
7005 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
7006 {
7007     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7008     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
7009     POINT Origin, Position, opt;
7010     LVITEMW lvItem;
7011     ITERATOR i;
7012     INT iItem;
7013     
7014     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
7015     
7016     lpht->flags = 0;
7017     lpht->iItem = -1;
7018     if (subitem) lpht->iSubItem = 0;
7019
7020     LISTVIEW_GetOrigin(infoPtr, &Origin);
7021
7022     /* set whole list relation flags */
7023     if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
7024     {
7025         /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7026         if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
7027             lpht->flags |= LVHT_TOLEFT;
7028
7029         if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7030             opt.y = lpht->pt.y + infoPtr->rcList.top;
7031         else
7032             opt.y = lpht->pt.y;
7033
7034         if (infoPtr->rcList.bottom < opt.y)
7035             lpht->flags |= LVHT_BELOW;
7036     }
7037     else
7038     {
7039         if (infoPtr->rcList.left > lpht->pt.x)
7040             lpht->flags |= LVHT_TOLEFT;
7041         else if (infoPtr->rcList.right < lpht->pt.x)
7042             lpht->flags |= LVHT_TORIGHT;
7043
7044         if (infoPtr->rcList.top > lpht->pt.y)
7045             lpht->flags |= LVHT_ABOVE;
7046         else if (infoPtr->rcList.bottom < lpht->pt.y)
7047             lpht->flags |= LVHT_BELOW;
7048     }
7049
7050     /* even if item is invalid try to find subitem */
7051     if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
7052     {
7053         RECT *pRect;
7054         INT j;
7055
7056         opt.x = lpht->pt.x - Origin.x;
7057
7058         lpht->iSubItem = -1;
7059         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
7060         {
7061             pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
7062
7063             if ((opt.x >= pRect->left) && (opt.x < pRect->right))
7064             {
7065                 lpht->iSubItem = j;
7066                 break;
7067             }
7068         }
7069         TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
7070     }
7071
7072     TRACE("lpht->flags=0x%x\n", lpht->flags);
7073     if (lpht->flags) return -1;
7074
7075     lpht->flags |= LVHT_NOWHERE;
7076
7077     /* first deal with the large items */
7078     rcSearch.left = lpht->pt.x;
7079     rcSearch.top = lpht->pt.y;
7080     rcSearch.right = rcSearch.left + 1;
7081     rcSearch.bottom = rcSearch.top + 1;
7082
7083     iterator_frameditems(&i, infoPtr, &rcSearch);
7084     iterator_next(&i); /* go to first item in the sequence */
7085     iItem = i.nItem;
7086     iterator_destroy(&i);
7087
7088     TRACE("lpht->iItem=%d\n", iItem);
7089     if (iItem == -1) return -1;
7090
7091     lvItem.mask = LVIF_STATE | LVIF_TEXT;
7092     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7093     lvItem.stateMask = LVIS_STATEIMAGEMASK;
7094     if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
7095     lvItem.iItem = iItem;
7096     lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
7097     lvItem.pszText = szDispText;
7098     lvItem.cchTextMax = DISP_TEXT_SIZE;
7099     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
7100     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
7101
7102     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7103     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
7104     opt.x = lpht->pt.x - Position.x - Origin.x;
7105
7106     if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7107         opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
7108     else
7109         opt.y = lpht->pt.y - Position.y - Origin.y;
7110
7111     if (infoPtr->uView == LV_VIEW_DETAILS)
7112     {
7113         rcBounds = rcBox;
7114         if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
7115             opt.x = lpht->pt.x - Origin.x;
7116     }
7117     else
7118     {
7119         UnionRect(&rcBounds, &rcIcon, &rcLabel);
7120         UnionRect(&rcBounds, &rcBounds, &rcState);
7121     }
7122     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
7123     if (!PtInRect(&rcBounds, opt)) return -1;
7124
7125     if (PtInRect(&rcIcon, opt))
7126         lpht->flags |= LVHT_ONITEMICON;
7127     else if (PtInRect(&rcLabel, opt))
7128         lpht->flags |= LVHT_ONITEMLABEL;
7129     else if (infoPtr->himlState && PtInRect(&rcState, opt))
7130         lpht->flags |= LVHT_ONITEMSTATEICON;
7131     /* special case for LVS_EX_FULLROWSELECT */
7132     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
7133       !(lpht->flags & LVHT_ONITEM))
7134     {
7135         lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
7136     }
7137     if (lpht->flags & LVHT_ONITEM)
7138         lpht->flags &= ~LVHT_NOWHERE;
7139     TRACE("lpht->flags=0x%x\n", lpht->flags); 
7140
7141     if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
7142                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
7143                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
7144     {
7145         if (infoPtr->uView == LV_VIEW_DETAILS)
7146         {
7147             /* get main item bounds */
7148             lvItem.iSubItem = 0;
7149             LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7150             UnionRect(&rcBounds, &rcIcon, &rcLabel);
7151             UnionRect(&rcBounds, &rcBounds, &rcState);
7152         }
7153         if (!PtInRect(&rcBounds, opt)) iItem = -1;
7154     }
7155     return lpht->iItem = iItem;
7156 }
7157
7158 /***
7159  * DESCRIPTION:
7160  * Inserts a new item in the listview control.
7161  *
7162  * PARAMETER(S):
7163  * [I] infoPtr : valid pointer to the listview structure
7164  * [I] lpLVItem : item information
7165  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7166  *
7167  * RETURN:
7168  *   SUCCESS : new item index
7169  *   FAILURE : -1
7170  */
7171 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
7172 {
7173     INT nItem;
7174     HDPA hdpaSubItems;
7175     NMLISTVIEW nmlv;
7176     ITEM_INFO *lpItem;
7177     ITEM_ID *lpID;
7178     BOOL is_sorted, has_changed;
7179     LVITEMW item;
7180     HWND hwndSelf = infoPtr->hwndSelf;
7181
7182     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
7183
7184     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
7185
7186     /* make sure it's an item, and not a subitem; cannot insert a subitem */
7187     if (!lpLVItem || lpLVItem->iSubItem) return -1;
7188
7189     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7190
7191     if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
7192     
7193     /* insert item in listview control data structure */
7194     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7195     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7196
7197     /* link with id struct */
7198     if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
7199     lpItem->id = lpID;
7200     lpID->item = hdpaSubItems;
7201     lpID->id = get_next_itemid(infoPtr);
7202     if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7203
7204     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7205                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7206
7207     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7208
7209     /* calculate new item index */
7210     if (is_sorted)
7211     {
7212         HDPA hItem;
7213         ITEM_INFO *item_s;
7214         INT i = 0, cmpv;
7215
7216         while (i < infoPtr->nItemCount)
7217         {
7218             hItem  = DPA_GetPtr( infoPtr->hdpaItems, i);
7219             item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
7220
7221             cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, TRUE);
7222             if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7223
7224             if (cmpv >= 0) break;
7225             i++;
7226         }
7227         nItem = i;
7228     }
7229     else
7230         nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7231
7232     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7233     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7234     if (nItem == -1) goto fail;
7235     infoPtr->nItemCount++;
7236
7237     /* shift indices first so they don't get tangled */
7238     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7239
7240     /* set the item attributes */
7241     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7242     {
7243         /* full size structure expected - _WIN32IE >= 0x560 */
7244         item = *lpLVItem;
7245     }
7246     else if (lpLVItem->mask & LVIF_INDENT)
7247     {
7248         /* indent member expected - _WIN32IE >= 0x300 */
7249         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7250     }
7251     else
7252     {
7253         /* minimal structure expected */
7254         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7255     }
7256     item.iItem = nItem;
7257     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7258     {
7259         item.mask |= LVIF_STATE;
7260         item.stateMask |= LVIS_STATEIMAGEMASK;
7261         item.state &= ~LVIS_STATEIMAGEMASK;
7262         item.state |= INDEXTOSTATEIMAGEMASK(1);
7263     }
7264     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7265
7266     /* make room for the position, if we are in the right mode */
7267     if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7268     {
7269         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7270             goto undo;
7271         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7272         {
7273             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7274             goto undo;
7275         }
7276     }
7277     
7278     /* send LVN_INSERTITEM notification */
7279     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7280     nmlv.iItem = nItem;
7281     nmlv.lParam = lpItem->lParam;
7282     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7283     if (!IsWindow(hwndSelf))
7284         return -1;
7285
7286     /* align items (set position of each item) */
7287     if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7288     {
7289         POINT pt;
7290
7291         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7292             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7293         else
7294             LISTVIEW_NextIconPosTop(infoPtr, &pt);
7295
7296         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7297     }
7298
7299     /* now is the invalidation fun */
7300     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7301     return nItem;
7302
7303 undo:
7304     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7305     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7306     infoPtr->nItemCount--;
7307 fail:
7308     DPA_DeletePtr(hdpaSubItems, 0);
7309     DPA_Destroy (hdpaSubItems);
7310     Free (lpItem);
7311     return -1;
7312 }
7313
7314 /***
7315  * DESCRIPTION:
7316  * Checks item visibility.
7317  *
7318  * PARAMETER(S):
7319  * [I] infoPtr : valid pointer to the listview structure
7320  * [I] nFirst : item index to check for
7321  *
7322  * RETURN:
7323  *   Item visible : TRUE
7324  *   Item invisible or failure : FALSE
7325  */
7326 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7327 {
7328     POINT Origin, Position;
7329     RECT rcItem;
7330     HDC hdc;
7331     BOOL ret;
7332
7333     TRACE("nItem=%d\n", nItem);
7334
7335     if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7336
7337     LISTVIEW_GetOrigin(infoPtr, &Origin);
7338     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7339     rcItem.left = Position.x + Origin.x;
7340     rcItem.top  = Position.y + Origin.y;
7341     rcItem.right  = rcItem.left + infoPtr->nItemWidth;
7342     rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7343
7344     hdc = GetDC(infoPtr->hwndSelf);
7345     if (!hdc) return FALSE;
7346     ret = RectVisible(hdc, &rcItem);
7347     ReleaseDC(infoPtr->hwndSelf, hdc);
7348
7349     return ret;
7350 }
7351
7352 /***
7353  * DESCRIPTION:
7354  * Redraws a range of items.
7355  *
7356  * PARAMETER(S):
7357  * [I] infoPtr : valid pointer to the listview structure
7358  * [I] nFirst : first item
7359  * [I] nLast : last item
7360  *
7361  * RETURN:
7362  *   SUCCESS : TRUE
7363  *   FAILURE : FALSE
7364  */
7365 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
7366 {
7367     INT i;
7368  
7369     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
7370         max(nFirst, nLast) >= infoPtr->nItemCount)
7371         return FALSE;
7372     
7373     for (i = nFirst; i <= nLast; i++)
7374         LISTVIEW_InvalidateItem(infoPtr, i);
7375
7376     return TRUE;
7377 }
7378
7379 /***
7380  * DESCRIPTION:
7381  * Scroll the content of a listview.
7382  *
7383  * PARAMETER(S):
7384  * [I] infoPtr : valid pointer to the listview structure
7385  * [I] dx : horizontal scroll amount in pixels
7386  * [I] dy : vertical scroll amount in pixels
7387  *
7388  * RETURN:
7389  *   SUCCESS : TRUE
7390  *   FAILURE : FALSE
7391  *
7392  * COMMENTS:
7393  *  If the control is in report view (LV_VIEW_DETAILS) the control can
7394  *  be scrolled only in line increments. "dy" will be rounded to the
7395  *  nearest number of pixels that are a whole line. Ex: if line height
7396  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7397  *  is passed, then the scroll will be 0.  (per MSDN 7/2002)
7398  *
7399  *  For:  (per experimentation with native control and CSpy ListView)
7400  *     LV_VIEW_ICON       dy=1 = 1 pixel  (vertical only)
7401  *                        dx ignored
7402  *     LV_VIEW_SMALLICON  dy=1 = 1 pixel  (vertical only)
7403  *                        dx ignored
7404  *     LV_VIEW_LIST       dx=1 = 1 column (horizontal only)
7405  *                           but will only scroll 1 column per message
7406  *                           no matter what the value.
7407  *                        dy must be 0 or FALSE returned.
7408  *     LV_VIEW_DETAILS    dx=1 = 1 pixel
7409  *                        dy=  see above
7410  *
7411  */
7412 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7413 {
7414     switch(infoPtr->uView) {
7415     case LV_VIEW_DETAILS:
7416         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
7417         dy /= infoPtr->nItemHeight;
7418         break;
7419     case LV_VIEW_LIST:
7420         if (dy != 0) return FALSE;
7421         break;
7422     default: /* icon */
7423         dx = 0;
7424         break;
7425     }   
7426
7427     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
7428     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
7429   
7430     return TRUE;
7431 }
7432
7433 /***
7434  * DESCRIPTION:
7435  * Sets the background color.
7436  *
7437  * PARAMETER(S):
7438  * [I] infoPtr : valid pointer to the listview structure
7439  * [I] clrBk : background color
7440  *
7441  * RETURN:
7442  *   SUCCESS : TRUE
7443  *   FAILURE : FALSE
7444  */
7445 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
7446 {
7447     TRACE("(clrBk=%x)\n", clrBk);
7448
7449     if(infoPtr->clrBk != clrBk) {
7450         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7451         infoPtr->clrBk = clrBk;
7452         if (clrBk == CLR_NONE)
7453             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
7454         else
7455         {
7456             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
7457             infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND;
7458         }
7459         LISTVIEW_InvalidateList(infoPtr);
7460     }
7461
7462    return TRUE;
7463 }
7464
7465 /* LISTVIEW_SetBkImage */
7466
7467 /*** Helper for {Insert,Set}ColumnT *only* */
7468 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
7469                                const LVCOLUMNW *lpColumn, BOOL isW)
7470 {
7471     if (lpColumn->mask & LVCF_FMT)
7472     {
7473         /* format member is valid */
7474         lphdi->mask |= HDI_FORMAT;
7475
7476         /* set text alignment (leftmost column must be left-aligned) */
7477         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7478             lphdi->fmt |= HDF_LEFT;
7479         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
7480             lphdi->fmt |= HDF_RIGHT;
7481         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
7482             lphdi->fmt |= HDF_CENTER;
7483
7484         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7485             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
7486
7487         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7488         {
7489             lphdi->fmt |= HDF_IMAGE;
7490             lphdi->iImage = I_IMAGECALLBACK;
7491         }
7492
7493         if (lpColumn->fmt & LVCFMT_FIXED_WIDTH)
7494             lphdi->fmt |= HDF_FIXEDWIDTH;
7495     }
7496
7497     if (lpColumn->mask & LVCF_WIDTH)
7498     {
7499         lphdi->mask |= HDI_WIDTH;
7500         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7501         {
7502             /* make it fill the remainder of the controls width */
7503             RECT rcHeader;
7504             INT item_index;
7505
7506             for(item_index = 0; item_index < (nColumn - 1); item_index++)
7507             {
7508                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7509                 lphdi->cxy += rcHeader.right - rcHeader.left;
7510             }
7511
7512             /* retrieve the layout of the header */
7513             GetClientRect(infoPtr->hwndSelf, &rcHeader);
7514             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7515
7516             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7517         }
7518         else
7519             lphdi->cxy = lpColumn->cx;
7520     }
7521
7522     if (lpColumn->mask & LVCF_TEXT)
7523     {
7524         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7525         lphdi->fmt |= HDF_STRING;
7526         lphdi->pszText = lpColumn->pszText;
7527         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7528     }
7529
7530     if (lpColumn->mask & LVCF_IMAGE)
7531     {
7532         lphdi->mask |= HDI_IMAGE;
7533         lphdi->iImage = lpColumn->iImage;
7534     }
7535
7536     if (lpColumn->mask & LVCF_ORDER)
7537     {
7538         lphdi->mask |= HDI_ORDER;
7539         lphdi->iOrder = lpColumn->iOrder;
7540     }
7541 }
7542
7543
7544 /***
7545  * DESCRIPTION:
7546  * Inserts a new column.
7547  *
7548  * PARAMETER(S):
7549  * [I] infoPtr : valid pointer to the listview structure
7550  * [I] nColumn : column index
7551  * [I] lpColumn : column information
7552  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7553  *
7554  * RETURN:
7555  *   SUCCESS : new column index
7556  *   FAILURE : -1
7557  */
7558 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7559                                   const LVCOLUMNW *lpColumn, BOOL isW)
7560 {
7561     COLUMN_INFO *lpColumnInfo;
7562     INT nNewColumn;
7563     HDITEMW hdi;
7564
7565     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7566
7567     if (!lpColumn || nColumn < 0) return -1;
7568     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7569     
7570     ZeroMemory(&hdi, sizeof(HDITEMW));
7571     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7572
7573     /*
7574      * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7575      * (can be seen in SPY) otherwise column never gets added.
7576      */
7577     if (!(lpColumn->mask & LVCF_WIDTH)) {
7578         hdi.mask |= HDI_WIDTH;
7579         hdi.cxy = 10;
7580     }
7581
7582     /*
7583      * when the iSubItem is available Windows copies it to the header lParam. It seems
7584      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7585      */
7586     if (lpColumn->mask & LVCF_SUBITEM)
7587     {
7588         hdi.mask |= HDI_LPARAM;
7589         hdi.lParam = lpColumn->iSubItem;
7590     }
7591
7592     /* create header if not present */
7593     LISTVIEW_CreateHeader(infoPtr);
7594     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7595          (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7596     {
7597         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7598     }
7599
7600     /* insert item in header control */
7601     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
7602                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7603                               (WPARAM)nColumn, (LPARAM)&hdi);
7604     if (nNewColumn == -1) return -1;
7605     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7606    
7607     /* create our own column info */ 
7608     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7609     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7610
7611     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7612     if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin;
7613     if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7614         goto fail;
7615
7616     /* now we have to actually adjust the data */
7617     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7618     {
7619         SUBITEM_INFO *lpSubItem;
7620         HDPA hdpaSubItems;
7621         INT nItem, i;
7622         
7623         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7624         {
7625             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7626             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7627             {
7628                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7629                 if (lpSubItem->iSubItem >= nNewColumn)
7630                     lpSubItem->iSubItem++;
7631             }
7632         }
7633     }
7634
7635     /* make space for the new column */
7636     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7637     LISTVIEW_UpdateItemSize(infoPtr);
7638     
7639     return nNewColumn;
7640
7641 fail:
7642     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7643     if (lpColumnInfo)
7644     {
7645         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7646         Free(lpColumnInfo);
7647     }
7648     return -1;
7649 }
7650
7651 /***
7652  * DESCRIPTION:
7653  * Sets the attributes of a header item.
7654  *
7655  * PARAMETER(S):
7656  * [I] infoPtr : valid pointer to the listview structure
7657  * [I] nColumn : column index
7658  * [I] lpColumn : column attributes
7659  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7660  *
7661  * RETURN:
7662  *   SUCCESS : TRUE
7663  *   FAILURE : FALSE
7664  */
7665 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7666                                 const LVCOLUMNW *lpColumn, BOOL isW)
7667 {
7668     HDITEMW hdi, hdiget;
7669     BOOL bResult;
7670
7671     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7672     
7673     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7674
7675     ZeroMemory(&hdi, sizeof(HDITEMW));
7676     if (lpColumn->mask & LVCF_FMT)
7677     {
7678         hdi.mask |= HDI_FORMAT;
7679         hdiget.mask = HDI_FORMAT;
7680         if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
7681             hdi.fmt = hdiget.fmt & HDF_STRING;
7682     }
7683     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7684
7685     /* set header item attributes */
7686     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7687     if (!bResult) return FALSE;
7688
7689     if (lpColumn->mask & LVCF_FMT)
7690     {
7691         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7692         INT oldFmt = lpColumnInfo->fmt;
7693         
7694         lpColumnInfo->fmt = lpColumn->fmt;
7695         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7696         {
7697             if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7698         }
7699     }
7700
7701     if (lpColumn->mask & LVCF_MINWIDTH)
7702         LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin;
7703
7704     return TRUE;
7705 }
7706
7707 /***
7708  * DESCRIPTION:
7709  * Sets the column order array
7710  *
7711  * PARAMETERS:
7712  * [I] infoPtr : valid pointer to the listview structure
7713  * [I] iCount : number of elements in column order array
7714  * [I] lpiArray : pointer to column order array
7715  *
7716  * RETURN:
7717  *   SUCCESS : TRUE
7718  *   FAILURE : FALSE
7719  */
7720 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7721 {
7722     TRACE("iCount %d lpiArray %p\n", iCount, lpiArray);
7723
7724     if (!lpiArray || !IsWindow(infoPtr->hwndHeader)) return FALSE;
7725
7726     infoPtr->colRectsDirty = TRUE;
7727
7728     return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray);
7729 }
7730
7731 /***
7732  * DESCRIPTION:
7733  * Sets the width of a column
7734  *
7735  * PARAMETERS:
7736  * [I] infoPtr : valid pointer to the listview structure
7737  * [I] nColumn : column index
7738  * [I] cx : column width
7739  *
7740  * RETURN:
7741  *   SUCCESS : TRUE
7742  *   FAILURE : FALSE
7743  */
7744 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7745 {
7746     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7747     INT max_cx = 0;
7748     HDITEMW hdi;
7749
7750     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7751
7752     /* set column width only if in report or list mode */
7753     if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7754
7755     /* take care of invalid cx values */
7756     if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
7757     else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
7758
7759     /* resize all columns if in LV_VIEW_LIST mode */
7760     if(infoPtr->uView == LV_VIEW_LIST)
7761     {
7762         infoPtr->nItemWidth = cx;
7763         LISTVIEW_InvalidateList(infoPtr);
7764         return TRUE;
7765     }
7766
7767     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7768
7769     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7770     {
7771         INT nLabelWidth;
7772         LVITEMW lvItem;
7773
7774         lvItem.mask = LVIF_TEXT;        
7775         lvItem.iItem = 0;
7776         lvItem.iSubItem = nColumn;
7777         lvItem.pszText = szDispText;
7778         lvItem.cchTextMax = DISP_TEXT_SIZE;
7779         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7780         {
7781             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7782             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7783             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7784         }
7785         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7786             max_cx += infoPtr->iconSize.cx;
7787         max_cx += TRAILING_LABEL_PADDING;
7788     }
7789
7790     /* autosize based on listview items width */
7791     if(cx == LVSCW_AUTOSIZE)
7792         cx = max_cx;
7793     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7794     {
7795         /* if iCol is the last column make it fill the remainder of the controls width */
7796         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
7797         {
7798             RECT rcHeader;
7799             POINT Origin;
7800
7801             LISTVIEW_GetOrigin(infoPtr, &Origin);
7802             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7803
7804             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7805         }
7806         else
7807         {
7808             /* Despite what the MS docs say, if this is not the last
7809                column, then MS resizes the column to the width of the
7810                largest text string in the column, including headers
7811                and items. This is different from LVSCW_AUTOSIZE in that
7812                LVSCW_AUTOSIZE ignores the header string length. */
7813             cx = 0;
7814
7815             /* retrieve header text */
7816             hdi.mask = HDI_TEXT;
7817             hdi.cchTextMax = DISP_TEXT_SIZE;
7818             hdi.pszText = szDispText;
7819             if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
7820             {
7821                 HDC hdc = GetDC(infoPtr->hwndSelf);
7822                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7823                 SIZE size;
7824
7825                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7826                     cx = size.cx + TRAILING_HEADER_PADDING;
7827                 /* FIXME: Take into account the header image, if one is present */
7828                 SelectObject(hdc, old_font);
7829                 ReleaseDC(infoPtr->hwndSelf, hdc);
7830             }
7831             cx = max (cx, max_cx);
7832         }
7833     }
7834
7835     if (cx < 0) return FALSE;
7836
7837     /* call header to update the column change */
7838     hdi.mask = HDI_WIDTH;
7839     hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin);
7840     TRACE("hdi.cxy=%d\n", hdi.cxy);
7841     return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
7842 }
7843
7844 /***
7845  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
7846  *
7847  */
7848 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7849 {
7850     HDC hdc_wnd, hdc;
7851     HBITMAP hbm_im, hbm_mask, hbm_orig;
7852     RECT rc;
7853     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7854     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7855     HIMAGELIST himl;
7856
7857     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7858                             ILC_COLOR | ILC_MASK, 2, 2);
7859     hdc_wnd = GetDC(infoPtr->hwndSelf);
7860     hdc = CreateCompatibleDC(hdc_wnd);
7861     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7862     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7863     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7864
7865     rc.left = rc.top = 0;
7866     rc.right = GetSystemMetrics(SM_CXSMICON);
7867     rc.bottom = GetSystemMetrics(SM_CYSMICON);
7868
7869     hbm_orig = SelectObject(hdc, hbm_mask);
7870     FillRect(hdc, &rc, hbr_white);
7871     InflateRect(&rc, -2, -2);
7872     FillRect(hdc, &rc, hbr_black);
7873
7874     SelectObject(hdc, hbm_im);
7875     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7876     SelectObject(hdc, hbm_orig);
7877     ImageList_Add(himl, hbm_im, hbm_mask); 
7878
7879     SelectObject(hdc, hbm_im);
7880     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7881     SelectObject(hdc, hbm_orig);
7882     ImageList_Add(himl, hbm_im, hbm_mask);
7883
7884     DeleteObject(hbm_mask);
7885     DeleteObject(hbm_im);
7886     DeleteDC(hdc);
7887
7888     return himl;
7889 }
7890
7891 /***
7892  * DESCRIPTION:
7893  * Sets the extended listview style.
7894  *
7895  * PARAMETERS:
7896  * [I] infoPtr : valid pointer to the listview structure
7897  * [I] dwMask : mask
7898  * [I] dwStyle : style
7899  *
7900  * RETURN:
7901  *   SUCCESS : previous style
7902  *   FAILURE : 0
7903  */
7904 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7905 {
7906     DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7907
7908     /* set new style */
7909     if (dwMask)
7910         infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7911     else
7912         infoPtr->dwLvExStyle = dwExStyle;
7913
7914     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7915     {
7916         HIMAGELIST himl = 0;
7917         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7918         {
7919             LVITEMW item;
7920             item.mask = LVIF_STATE;
7921             item.stateMask = LVIS_STATEIMAGEMASK;
7922             item.state = INDEXTOSTATEIMAGEMASK(1);
7923             LISTVIEW_SetItemState(infoPtr, -1, &item);
7924
7925             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7926         }
7927         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7928     }
7929     
7930     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7931     {
7932         DWORD dwStyle;
7933
7934         /* if not already created */
7935         LISTVIEW_CreateHeader(infoPtr);
7936
7937         dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7938         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7939             dwStyle |= HDS_DRAGDROP;
7940         else
7941             dwStyle &= ~HDS_DRAGDROP;
7942         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7943     }
7944
7945     /* GRIDLINES adds decoration at top so changes sizes */
7946     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7947     {
7948         LISTVIEW_UpdateSize(infoPtr);
7949     }
7950
7951     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_TRANSPARENTBKGND)
7952     {
7953         if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
7954             LISTVIEW_SetBkColor(infoPtr, CLR_NONE);
7955     }
7956
7957     LISTVIEW_InvalidateList(infoPtr);
7958     return dwOldExStyle;
7959 }
7960
7961 /***
7962  * DESCRIPTION:
7963  * Sets the new hot cursor used during hot tracking and hover selection.
7964  *
7965  * PARAMETER(S):
7966  * [I] infoPtr : valid pointer to the listview structure
7967  * [I] hCursor : the new hot cursor handle
7968  *
7969  * RETURN:
7970  * Returns the previous hot cursor
7971  */
7972 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7973 {
7974     HCURSOR oldCursor = infoPtr->hHotCursor;
7975     
7976     infoPtr->hHotCursor = hCursor;
7977
7978     return oldCursor;
7979 }
7980
7981
7982 /***
7983  * DESCRIPTION:
7984  * Sets the hot item index.
7985  *
7986  * PARAMETERS:
7987  * [I] infoPtr : valid pointer to the listview structure
7988  * [I] iIndex : index
7989  *
7990  * RETURN:
7991  *   SUCCESS : previous hot item index
7992  *   FAILURE : -1 (no hot item)
7993  */
7994 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7995 {
7996     INT iOldIndex = infoPtr->nHotItem;
7997     
7998     infoPtr->nHotItem = iIndex;
7999     
8000     return iOldIndex;
8001 }
8002
8003
8004 /***
8005  * DESCRIPTION:
8006  * Sets the amount of time the cursor must hover over an item before it is selected.
8007  *
8008  * PARAMETER(S):
8009  * [I] infoPtr : valid pointer to the listview structure
8010  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8011  *
8012  * RETURN:
8013  * Returns the previous hover time
8014  */
8015 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
8016 {
8017     DWORD oldHoverTime = infoPtr->dwHoverTime;
8018     
8019     infoPtr->dwHoverTime = dwHoverTime;
8020     
8021     return oldHoverTime;
8022 }
8023
8024 /***
8025  * DESCRIPTION:
8026  * Sets spacing for icons of LVS_ICON style.
8027  *
8028  * PARAMETER(S):
8029  * [I] infoPtr : valid pointer to the listview structure
8030  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8031  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8032  *
8033  * RETURN:
8034  *   MAKELONG(oldcx, oldcy)
8035  */
8036 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
8037 {
8038     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
8039
8040     TRACE("requested=(%d,%d)\n", cx, cy);
8041     
8042     /* this is supported only for LVS_ICON style */
8043     if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
8044   
8045     /* set to defaults, if instructed to */
8046     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
8047     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
8048
8049     /* if 0 then compute width
8050      * FIXME: Should scan each item and determine max width of
8051      *        icon or label, then make that the width */
8052     if (cx == 0)
8053         cx = infoPtr->iconSpacing.cx;
8054
8055     /* if 0 then compute height */
8056     if (cy == 0) 
8057         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
8058              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
8059     
8060
8061     infoPtr->iconSpacing.cx = cx;
8062     infoPtr->iconSpacing.cy = cy;
8063
8064     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8065           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
8066           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
8067           infoPtr->ntmHeight);
8068
8069     /* these depend on the iconSpacing */
8070     LISTVIEW_UpdateItemSize(infoPtr);
8071
8072     return oldspacing;
8073 }
8074
8075 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
8076 {
8077     INT cx, cy;
8078     
8079     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
8080     {
8081         size->cx = cx;
8082         size->cy = cy;
8083     }
8084     else
8085     {
8086         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
8087         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
8088     }
8089 }
8090
8091 /***
8092  * DESCRIPTION:
8093  * Sets image lists.
8094  *
8095  * PARAMETER(S):
8096  * [I] infoPtr : valid pointer to the listview structure
8097  * [I] nType : image list type
8098  * [I] himl : image list handle
8099  *
8100  * RETURN:
8101  *   SUCCESS : old image list
8102  *   FAILURE : NULL
8103  */
8104 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
8105 {
8106     INT oldHeight = infoPtr->nItemHeight;
8107     HIMAGELIST himlOld = 0;
8108
8109     TRACE("(nType=%d, himl=%p\n", nType, himl);
8110
8111     switch (nType)
8112     {
8113     case LVSIL_NORMAL:
8114         himlOld = infoPtr->himlNormal;
8115         infoPtr->himlNormal = himl;
8116         if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
8117         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8118     break;
8119
8120     case LVSIL_SMALL:
8121         himlOld = infoPtr->himlSmall;
8122         infoPtr->himlSmall = himl;
8123         if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
8124     break;
8125
8126     case LVSIL_STATE:
8127         himlOld = infoPtr->himlState;
8128         infoPtr->himlState = himl;
8129         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
8130         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
8131     break;
8132
8133     default:
8134         ERR("Unknown icon type=%d\n", nType);
8135         return NULL;
8136     }
8137
8138     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
8139     if (infoPtr->nItemHeight != oldHeight)
8140         LISTVIEW_UpdateScroll(infoPtr);
8141
8142     return himlOld;
8143 }
8144
8145 /***
8146  * DESCRIPTION:
8147  * Preallocates memory (does *not* set the actual count of items !)
8148  *
8149  * PARAMETER(S):
8150  * [I] infoPtr : valid pointer to the listview structure
8151  * [I] nItems : item count (projected number of items to allocate)
8152  * [I] dwFlags : update flags
8153  *
8154  * RETURN:
8155  *   SUCCESS : TRUE
8156  *   FAILURE : FALSE
8157  */
8158 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
8159 {
8160     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
8161
8162     if (infoPtr->dwStyle & LVS_OWNERDATA)
8163     {
8164         INT nOldCount = infoPtr->nItemCount;
8165
8166         if (nItems < nOldCount)
8167         {
8168             RANGE range = { nItems, nOldCount };
8169             ranges_del(infoPtr->selectionRanges, range);
8170             if (infoPtr->nFocusedItem >= nItems)
8171             {
8172                 LISTVIEW_SetItemFocus(infoPtr, -1);
8173                 SetRectEmpty(&infoPtr->rcFocus);
8174             }
8175         }
8176
8177         infoPtr->nItemCount = nItems;
8178         LISTVIEW_UpdateScroll(infoPtr);
8179
8180         /* the flags are valid only in ownerdata report and list modes */
8181         if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
8182
8183         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
8184             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
8185
8186         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
8187             LISTVIEW_InvalidateList(infoPtr);
8188         else
8189         {
8190             INT nFrom, nTo;
8191             POINT Origin;
8192             RECT rcErase;
8193             
8194             LISTVIEW_GetOrigin(infoPtr, &Origin);
8195             nFrom = min(nOldCount, nItems);
8196             nTo = max(nOldCount, nItems);
8197     
8198             if (infoPtr->uView == LV_VIEW_DETAILS)
8199             {
8200                 rcErase.left = 0;
8201                 rcErase.top = nFrom * infoPtr->nItemHeight;
8202                 rcErase.right = infoPtr->nItemWidth;
8203                 rcErase.bottom = nTo * infoPtr->nItemHeight;
8204                 OffsetRect(&rcErase, Origin.x, Origin.y);
8205                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8206                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8207             }
8208             else /* LV_VIEW_LIST */
8209             {
8210                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
8211
8212                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
8213                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
8214                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
8215                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8216                 OffsetRect(&rcErase, Origin.x, Origin.y);
8217                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8218                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8219
8220                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
8221                 rcErase.top = 0;
8222                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
8223                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8224                 OffsetRect(&rcErase, Origin.x, Origin.y);
8225                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8226                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8227             }
8228         }
8229     }
8230     else
8231     {
8232         /* According to MSDN for non-LVS_OWNERDATA this is just
8233          * a performance issue. The control allocates its internal
8234          * data structures for the number of items specified. It
8235          * cuts down on the number of memory allocations. Therefore
8236          * we will just issue a WARN here
8237          */
8238         WARN("for non-ownerdata performance option not implemented.\n");
8239     }
8240
8241     return TRUE;
8242 }
8243
8244 /***
8245  * DESCRIPTION:
8246  * Sets the position of an item.
8247  *
8248  * PARAMETER(S):
8249  * [I] infoPtr : valid pointer to the listview structure
8250  * [I] nItem : item index
8251  * [I] pt : coordinate
8252  *
8253  * RETURN:
8254  *   SUCCESS : TRUE
8255  *   FAILURE : FALSE
8256  */
8257 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
8258 {
8259     POINT Origin;
8260
8261     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
8262
8263     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
8264         !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
8265
8266     LISTVIEW_GetOrigin(infoPtr, &Origin);
8267
8268     /* This point value seems to be an undocumented feature.
8269      * The best guess is that it means either at the origin, 
8270      * or at true beginning of the list. I will assume the origin. */
8271     if ((pt.x == -1) && (pt.y == -1))
8272         pt = Origin;
8273     
8274     if (infoPtr->uView == LV_VIEW_ICON)
8275     {
8276         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
8277         pt.y -= ICON_TOP_PADDING;
8278     }
8279     pt.x -= Origin.x;
8280     pt.y -= Origin.y;
8281
8282     infoPtr->bAutoarrange = FALSE;
8283
8284     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
8285 }
8286
8287 /***
8288  * DESCRIPTION:
8289  * Sets the state of one or many items.
8290  *
8291  * PARAMETER(S):
8292  * [I] infoPtr : valid pointer to the listview structure
8293  * [I] nItem : item index
8294  * [I] lpLVItem : item or subitem info
8295  *
8296  * RETURN:
8297  *   SUCCESS : TRUE
8298  *   FAILURE : FALSE
8299  */
8300 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
8301 {
8302     BOOL bResult = TRUE;
8303     LVITEMW lvItem;
8304
8305     lvItem.iItem = nItem;
8306     lvItem.iSubItem = 0;
8307     lvItem.mask = LVIF_STATE;
8308     lvItem.state = lpLVItem->state;
8309     lvItem.stateMask = lpLVItem->stateMask;
8310     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
8311
8312     if (nItem == -1)
8313     {
8314         /* select all isn't allowed in LVS_SINGLESEL */
8315         if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
8316             return FALSE;
8317
8318         /* focus all isn't allowed */
8319         if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
8320
8321         /* apply to all items */
8322         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8323             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
8324     }
8325     else
8326         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
8327
8328     return bResult;
8329 }
8330
8331 /***
8332  * DESCRIPTION:
8333  * Sets the text of an item or subitem.
8334  *
8335  * PARAMETER(S):
8336  * [I] hwnd : window handle
8337  * [I] nItem : item index
8338  * [I] lpLVItem : item or subitem info
8339  * [I] isW : TRUE if input is Unicode
8340  *
8341  * RETURN:
8342  *   SUCCESS : TRUE
8343  *   FAILURE : FALSE
8344  */
8345 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
8346 {
8347     LVITEMW lvItem;
8348
8349     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
8350     
8351     lvItem.iItem = nItem;
8352     lvItem.iSubItem = lpLVItem->iSubItem;
8353     lvItem.mask = LVIF_TEXT;
8354     lvItem.pszText = lpLVItem->pszText;
8355     lvItem.cchTextMax = lpLVItem->cchTextMax;
8356     
8357     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
8358
8359     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
8360 }
8361
8362 /***
8363  * DESCRIPTION:
8364  * Set item index that marks the start of a multiple selection.
8365  *
8366  * PARAMETER(S):
8367  * [I] infoPtr : valid pointer to the listview structure
8368  * [I] nIndex : index
8369  *
8370  * RETURN:
8371  * Index number or -1 if there is no selection mark.
8372  */
8373 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
8374 {
8375   INT nOldIndex = infoPtr->nSelectionMark;
8376
8377   TRACE("(nIndex=%d)\n", nIndex);
8378
8379   infoPtr->nSelectionMark = nIndex;
8380
8381   return nOldIndex;
8382 }
8383
8384 /***
8385  * DESCRIPTION:
8386  * Sets the text background color.
8387  *
8388  * PARAMETER(S):
8389  * [I] infoPtr : valid pointer to the listview structure
8390  * [I] clrTextBk : text background color
8391  *
8392  * RETURN:
8393  *   SUCCESS : TRUE
8394  *   FAILURE : FALSE
8395  */
8396 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
8397 {
8398     TRACE("(clrTextBk=%x)\n", clrTextBk);
8399
8400     if (infoPtr->clrTextBk != clrTextBk)
8401     {
8402         infoPtr->clrTextBk = clrTextBk;
8403         LISTVIEW_InvalidateList(infoPtr);
8404     }
8405     
8406   return TRUE;
8407 }
8408
8409 /***
8410  * DESCRIPTION:
8411  * Sets the text foreground color.
8412  *
8413  * PARAMETER(S):
8414  * [I] infoPtr : valid pointer to the listview structure
8415  * [I] clrText : text color
8416  *
8417  * RETURN:
8418  *   SUCCESS : TRUE
8419  *   FAILURE : FALSE
8420  */
8421 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
8422 {
8423     TRACE("(clrText=%x)\n", clrText);
8424
8425     if (infoPtr->clrText != clrText)
8426     {
8427         infoPtr->clrText = clrText;
8428         LISTVIEW_InvalidateList(infoPtr);
8429     }
8430
8431     return TRUE;
8432 }
8433
8434 /***
8435  * DESCRIPTION:
8436  * Sets new ToolTip window to ListView control.
8437  *
8438  * PARAMETER(S):
8439  * [I] infoPtr        : valid pointer to the listview structure
8440  * [I] hwndNewToolTip : handle to new ToolTip
8441  *
8442  * RETURN:
8443  *   old tool tip
8444  */
8445 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
8446 {
8447   HWND hwndOldToolTip = infoPtr->hwndToolTip;
8448   infoPtr->hwndToolTip = hwndNewToolTip;
8449   return hwndOldToolTip;
8450 }
8451
8452 /*
8453  * DESCRIPTION:
8454  *   sets the Unicode character format flag for the control
8455  * PARAMETER(S):
8456  *    [I] infoPtr         :valid pointer to the listview structure
8457  *    [I] fUnicode        :true to switch to UNICODE false to switch to ANSI
8458  *
8459  * RETURN:
8460  *    Old Unicode Format
8461  */
8462 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
8463 {
8464   SHORT rc = infoPtr->notifyFormat;
8465   infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
8466   return rc == NFR_UNICODE;
8467 }
8468
8469 /*
8470  * DESCRIPTION:
8471  *   sets the control view mode
8472  * PARAMETER(S):
8473  *    [I] infoPtr         :valid pointer to the listview structure
8474  *    [I] nView           :new view mode value
8475  *
8476  * RETURN:
8477  *    SUCCESS:  1
8478  *    FAILURE: -1
8479  */
8480 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8481 {
8482   SIZE oldIconSize = infoPtr->iconSize;
8483   HIMAGELIST himl;
8484
8485   if (infoPtr->uView == nView) return 1;
8486
8487   if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8488   if (nView == LV_VIEW_TILE)
8489   {
8490       FIXME("View LV_VIEW_TILE unimplemented\n");
8491       return -1;
8492   }
8493
8494   infoPtr->uView = nView;
8495
8496   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8497   ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8498
8499   ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8500   SetRectEmpty(&infoPtr->rcFocus);
8501
8502   himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8503   set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8504
8505   switch (nView)
8506   {
8507   case LV_VIEW_ICON:
8508       if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8509       {
8510             TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8511                    oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8512             LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8513       }
8514       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8515       break;
8516   case LV_VIEW_SMALLICON:
8517       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8518       break;
8519   case LV_VIEW_DETAILS:
8520   {
8521       HDLAYOUT hl;
8522       WINDOWPOS wp;
8523
8524       LISTVIEW_CreateHeader( infoPtr );
8525
8526       hl.prc = &infoPtr->rcList;
8527       hl.pwpos = &wp;
8528       SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8529       SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8530                    wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8531       break;
8532   }
8533   case LV_VIEW_LIST:
8534       break;
8535   }
8536
8537   LISTVIEW_UpdateItemSize(infoPtr);
8538   LISTVIEW_UpdateSize(infoPtr);
8539   LISTVIEW_UpdateScroll(infoPtr);
8540   LISTVIEW_InvalidateList(infoPtr);
8541
8542   TRACE("nView=%d\n", nView);
8543
8544   return 1;
8545 }
8546
8547 /* LISTVIEW_SetWorkAreas */
8548
8549 /***
8550  * DESCRIPTION:
8551  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8552  *
8553  * PARAMETER(S):
8554  * [I] first : pointer to first ITEM_INFO to compare
8555  * [I] second : pointer to second ITEM_INFO to compare
8556  * [I] lParam : HWND of control
8557  *
8558  * RETURN:
8559  *   if first comes before second : negative
8560  *   if first comes after second : positive
8561  *   if first and second are equivalent : zero
8562  */
8563 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8564 {
8565   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8566   ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8567   ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8568
8569   /* Forward the call to the client defined callback */
8570   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8571 }
8572
8573 /***
8574  * DESCRIPTION:
8575  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8576  *
8577  * PARAMETER(S):
8578  * [I] first : pointer to first ITEM_INFO to compare
8579  * [I] second : pointer to second ITEM_INFO to compare
8580  * [I] lParam : HWND of control
8581  *
8582  * RETURN:
8583  *   if first comes before second : negative
8584  *   if first comes after second : positive
8585  *   if first and second are equivalent : zero
8586  */
8587 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8588 {
8589   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8590   INT first_idx  = DPA_GetPtrIndex( infoPtr->hdpaItems, first  );
8591   INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8592
8593   /* Forward the call to the client defined callback */
8594   return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8595 }
8596
8597 /***
8598  * DESCRIPTION:
8599  * Sorts the listview items.
8600  *
8601  * PARAMETER(S):
8602  * [I] infoPtr : valid pointer to the listview structure
8603  * [I] pfnCompare : application-defined value
8604  * [I] lParamSort : pointer to comparison callback
8605  * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8606  *
8607  * RETURN:
8608  *   SUCCESS : TRUE
8609  *   FAILURE : FALSE
8610  */
8611 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8612                                LPARAM lParamSort, BOOL IsEx)
8613 {
8614     HDPA hdpaSubItems;
8615     ITEM_INFO *lpItem;
8616     LPVOID selectionMarkItem = NULL;
8617     LPVOID focusedItem = NULL;
8618     int i;
8619
8620     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8621
8622     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8623
8624     if (!pfnCompare) return FALSE;
8625     if (!infoPtr->hdpaItems) return FALSE;
8626
8627     /* if there are 0 or 1 items, there is no need to sort */
8628     if (infoPtr->nItemCount < 2) return TRUE;
8629
8630     /* clear selection */
8631     ranges_clear(infoPtr->selectionRanges);
8632
8633     /* save selection mark and focused item */
8634     if (infoPtr->nSelectionMark >= 0)
8635         selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8636     if (infoPtr->nFocusedItem >= 0)
8637         focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8638
8639     infoPtr->pfnCompare = pfnCompare;
8640     infoPtr->lParamSort = lParamSort;
8641     if (IsEx)
8642         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8643     else
8644         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8645
8646     /* restore selection ranges */
8647     for (i=0; i < infoPtr->nItemCount; i++)
8648     {
8649         hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8650         lpItem = DPA_GetPtr(hdpaSubItems, 0);
8651
8652         if (lpItem->state & LVIS_SELECTED)
8653             ranges_additem(infoPtr->selectionRanges, i);
8654     }
8655     /* restore selection mark and focused item */
8656     infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8657     infoPtr->nFocusedItem   = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8658
8659     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8660
8661     /* refresh the display */
8662     if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8663         LISTVIEW_InvalidateList(infoPtr);
8664
8665     return TRUE;
8666 }
8667
8668 /***
8669  * DESCRIPTION:
8670  * Update theme handle after a theme change.
8671  *
8672  * PARAMETER(S):
8673  * [I] infoPtr : valid pointer to the listview structure
8674  *
8675  * RETURN:
8676  *   SUCCESS : 0
8677  *   FAILURE : something else
8678  */
8679 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8680 {
8681     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8682     CloseThemeData(theme);
8683     OpenThemeData(infoPtr->hwndSelf, themeClass);
8684     return 0;
8685 }
8686
8687 /***
8688  * DESCRIPTION:
8689  * Updates an items or rearranges the listview control.
8690  *
8691  * PARAMETER(S):
8692  * [I] infoPtr : valid pointer to the listview structure
8693  * [I] nItem : item index
8694  *
8695  * RETURN:
8696  *   SUCCESS : TRUE
8697  *   FAILURE : FALSE
8698  */
8699 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8700 {
8701     TRACE("(nItem=%d)\n", nItem);
8702
8703     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8704
8705     /* rearrange with default alignment style */
8706     if (is_autoarrange(infoPtr))
8707         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8708     else
8709         LISTVIEW_InvalidateItem(infoPtr, nItem);
8710
8711     return TRUE;
8712 }
8713
8714 /***
8715  * DESCRIPTION:
8716  * Draw the track line at the place defined in the infoPtr structure.
8717  * The line is drawn with a XOR pen so drawing the line for the second time
8718  * in the same place erases the line.
8719  *
8720  * PARAMETER(S):
8721  * [I] infoPtr : valid pointer to the listview structure
8722  *
8723  * RETURN:
8724  *   SUCCESS : TRUE
8725  *   FAILURE : FALSE
8726  */
8727 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8728 {
8729     HPEN hOldPen;
8730     HDC hdc;
8731     INT oldROP;
8732
8733     if (infoPtr->xTrackLine == -1)
8734         return FALSE;
8735
8736     if (!(hdc = GetDC(infoPtr->hwndSelf)))
8737         return FALSE;
8738     hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8739     oldROP = SetROP2(hdc, R2_XORPEN);
8740     MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8741     LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8742     SetROP2(hdc, oldROP);
8743     SelectObject(hdc, hOldPen);
8744     ReleaseDC(infoPtr->hwndSelf, hdc);
8745     return TRUE;
8746 }
8747
8748 /***
8749  * DESCRIPTION:
8750  * Called when an edit control should be displayed. This function is called after
8751  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8752  *
8753  * PARAMETER(S):
8754  * [I] hwnd : Handle to the listview
8755  * [I] uMsg : WM_TIMER (ignored)
8756  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8757  * [I] dwTimer : The elapsed time (ignored)
8758  *
8759  * RETURN:
8760  *   None.
8761  */
8762 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8763 {
8764     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8765     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8766
8767     KillTimer(hwnd, idEvent);
8768     editItem->fEnabled = FALSE;
8769     /* check if the item is still selected */
8770     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8771         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8772 }
8773
8774 /***
8775  * DESCRIPTION:
8776  * Creates the listview control - the WM_NCCREATE phase.
8777  *
8778  * PARAMETER(S):
8779  * [I] hwnd : window handle
8780  * [I] lpcs : the create parameters
8781  *
8782  * RETURN:
8783  *   Success: TRUE
8784  *   Failure: FALSE
8785  */
8786 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8787 {
8788   LISTVIEW_INFO *infoPtr;
8789   LOGFONTW logFont;
8790
8791   TRACE("(lpcs=%p)\n", lpcs);
8792
8793   /* initialize info pointer */
8794   infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8795   if (!infoPtr) return FALSE;
8796
8797   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8798
8799   infoPtr->hwndSelf = hwnd;
8800   infoPtr->dwStyle = lpcs->style;    /* Note: may be changed in WM_CREATE */
8801   map_style_view(infoPtr);
8802   /* determine the type of structures to use */
8803   infoPtr->hwndNotify = lpcs->hwndParent;
8804   /* infoPtr->notifyFormat will be filled in WM_CREATE */
8805
8806   /* initialize color information  */
8807   infoPtr->clrBk = CLR_NONE;
8808   infoPtr->clrText = CLR_DEFAULT;
8809   infoPtr->clrTextBk = CLR_DEFAULT;
8810   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8811
8812   /* set default values */
8813   infoPtr->nFocusedItem = -1;
8814   infoPtr->nSelectionMark = -1;
8815   infoPtr->nHotItem = -1;
8816   infoPtr->bRedraw = TRUE;
8817   infoPtr->bNoItemMetrics = TRUE;
8818   infoPtr->bDoChangeNotify = TRUE;
8819   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8820   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8821   infoPtr->nEditLabelItem = -1;
8822   infoPtr->nLButtonDownItem = -1;
8823   infoPtr->dwHoverTime = -1; /* default system hover time */
8824   infoPtr->nMeasureItemHeight = 0;
8825   infoPtr->xTrackLine = -1;  /* no track line */
8826   infoPtr->itemEdit.fEnabled = FALSE;
8827   infoPtr->iVersion = COMCTL32_VERSION;
8828   infoPtr->colRectsDirty = FALSE;
8829
8830   /* get default font (icon title) */
8831   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8832   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8833   infoPtr->hFont = infoPtr->hDefaultFont;
8834   LISTVIEW_SaveTextMetrics(infoPtr);
8835
8836   /* allocate memory for the data structure */
8837   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8838   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8839   if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
8840   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
8841   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
8842   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8843   return TRUE;
8844
8845 fail:
8846     DestroyWindow(infoPtr->hwndHeader);
8847     ranges_destroy(infoPtr->selectionRanges);
8848     DPA_Destroy(infoPtr->hdpaItems);
8849     DPA_Destroy(infoPtr->hdpaItemIds);
8850     DPA_Destroy(infoPtr->hdpaPosX);
8851     DPA_Destroy(infoPtr->hdpaPosY);
8852     DPA_Destroy(infoPtr->hdpaColumns);
8853     Free(infoPtr);
8854     return FALSE;
8855 }
8856
8857 /***
8858  * DESCRIPTION:
8859  * Creates the listview control - the WM_CREATE phase. Most of the data is
8860  * already set up in LISTVIEW_NCCreate
8861  *
8862  * PARAMETER(S):
8863  * [I] hwnd : window handle
8864  * [I] lpcs : the create parameters
8865  *
8866  * RETURN:
8867  *   Success: 0
8868  *   Failure: -1
8869  */
8870 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8871 {
8872   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8873
8874   TRACE("(lpcs=%p)\n", lpcs);
8875
8876   infoPtr->dwStyle = lpcs->style;
8877   map_style_view(infoPtr);
8878
8879   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8880                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8881   /* on error defaulting to ANSI notifications */
8882   if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
8883
8884   if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
8885   {
8886     if (LISTVIEW_CreateHeader(infoPtr) < 0)  return -1;
8887   }
8888   else
8889     infoPtr->hwndHeader = 0;
8890
8891   /* init item size to avoid division by 0 */
8892   LISTVIEW_UpdateItemSize (infoPtr);
8893
8894   if (infoPtr->uView == LV_VIEW_DETAILS)
8895   {
8896     if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8897     {
8898       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8899     }
8900     LISTVIEW_UpdateScroll(infoPtr);
8901     /* send WM_MEASUREITEM notification */
8902     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
8903   }
8904
8905   OpenThemeData(hwnd, themeClass);
8906
8907   /* initialize the icon sizes */
8908   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
8909   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8910   return 0;
8911 }
8912
8913 /***
8914  * DESCRIPTION:
8915  * Destroys the listview control.
8916  *
8917  * PARAMETER(S):
8918  * [I] infoPtr : valid pointer to the listview structure
8919  *
8920  * RETURN:
8921  *   Success: 0
8922  *   Failure: -1
8923  */
8924 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8925 {
8926     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8927     CloseThemeData(theme);
8928     return 0;
8929 }
8930
8931 /***
8932  * DESCRIPTION:
8933  * Enables the listview control.
8934  *
8935  * PARAMETER(S):
8936  * [I] infoPtr : valid pointer to the listview structure
8937  * [I] bEnable : specifies whether to enable or disable the window
8938  *
8939  * RETURN:
8940  *   SUCCESS : TRUE
8941  *   FAILURE : FALSE
8942  */
8943 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8944 {
8945     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8946         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8947     return TRUE;
8948 }
8949
8950 /***
8951  * DESCRIPTION:
8952  * Erases the background of the listview control.
8953  *
8954  * PARAMETER(S):
8955  * [I] infoPtr : valid pointer to the listview structure
8956  * [I] hdc : device context handle
8957  *
8958  * RETURN:
8959  *   SUCCESS : TRUE
8960  *   FAILURE : FALSE
8961  */
8962 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8963 {
8964     RECT rc;
8965
8966     TRACE("(hdc=%p)\n", hdc);
8967
8968     if (!GetClipBox(hdc, &rc)) return FALSE;
8969
8970     if (infoPtr->clrBk == CLR_NONE)
8971     {
8972         if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
8973             return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT,
8974                                 (WPARAM)hdc, PRF_ERASEBKGND);
8975         else
8976             return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
8977     }
8978
8979     /* for double buffered controls we need to do this during refresh */
8980     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8981
8982     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8983 }
8984         
8985
8986 /***
8987  * DESCRIPTION:
8988  * Helper function for LISTVIEW_[HV]Scroll *only*.
8989  * Performs vertical/horizontal scrolling by a give amount.
8990  *
8991  * PARAMETER(S):
8992  * [I] infoPtr : valid pointer to the listview structure
8993  * [I] dx : amount of horizontal scroll
8994  * [I] dy : amount of vertical scroll
8995  */
8996 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8997 {
8998     /* now we can scroll the list */
8999     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
9000                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
9001     /* if we have focus, adjust rect */
9002     OffsetRect(&infoPtr->rcFocus, dx, dy);
9003     UpdateWindow(infoPtr->hwndSelf);
9004 }
9005
9006 /***
9007  * DESCRIPTION:
9008  * Performs vertical scrolling.
9009  *
9010  * PARAMETER(S):
9011  * [I] infoPtr : valid pointer to the listview structure
9012  * [I] nScrollCode : scroll code
9013  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9014  * [I] hScrollWnd  : scrollbar control window handle
9015  *
9016  * RETURN:
9017  * Zero
9018  *
9019  * NOTES:
9020  *   SB_LINEUP/SB_LINEDOWN:
9021  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
9022  *        for LVS_REPORT is 1 line
9023  *        for LVS_LIST cannot occur
9024  *
9025  */
9026 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
9027                                 INT nScrollDiff, HWND hScrollWnd)
9028 {
9029     INT nOldScrollPos, nNewScrollPos;
9030     SCROLLINFO scrollInfo;
9031     BOOL is_an_icon;
9032
9033     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
9034         debugscrollcode(nScrollCode), nScrollDiff);
9035
9036     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9037
9038     scrollInfo.cbSize = sizeof(SCROLLINFO);
9039     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9040
9041     is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9042
9043     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
9044
9045     nOldScrollPos = scrollInfo.nPos;
9046     switch (nScrollCode)
9047     {
9048     case SB_INTERNAL:
9049         break;
9050
9051     case SB_LINEUP:
9052         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9053         break;
9054
9055     case SB_LINEDOWN:
9056         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9057         break;
9058
9059     case SB_PAGEUP:
9060         nScrollDiff = -scrollInfo.nPage;
9061         break;
9062
9063     case SB_PAGEDOWN:
9064         nScrollDiff = scrollInfo.nPage;
9065         break;
9066
9067     case SB_THUMBPOSITION:
9068     case SB_THUMBTRACK:
9069         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9070         break;
9071
9072     default:
9073         nScrollDiff = 0;
9074     }
9075
9076     /* quit right away if pos isn't changing */
9077     if (nScrollDiff == 0) return 0;
9078     
9079     /* calculate new position, and handle overflows */
9080     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9081     if (nScrollDiff > 0) {
9082         if (nNewScrollPos < nOldScrollPos ||
9083             nNewScrollPos > scrollInfo.nMax)
9084             nNewScrollPos = scrollInfo.nMax;
9085     } else {
9086         if (nNewScrollPos > nOldScrollPos ||
9087             nNewScrollPos < scrollInfo.nMin)
9088             nNewScrollPos = scrollInfo.nMin;
9089     }
9090
9091     /* set the new position, and reread in case it changed */
9092     scrollInfo.fMask = SIF_POS;
9093     scrollInfo.nPos = nNewScrollPos;
9094     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
9095     
9096     /* carry on only if it really changed */
9097     if (nNewScrollPos == nOldScrollPos) return 0;
9098     
9099     /* now adjust to client coordinates */
9100     nScrollDiff = nOldScrollPos - nNewScrollPos;
9101     if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
9102    
9103     /* and scroll the window */ 
9104     scroll_list(infoPtr, 0, nScrollDiff);
9105
9106     return 0;
9107 }
9108
9109 /***
9110  * DESCRIPTION:
9111  * Performs horizontal scrolling.
9112  *
9113  * PARAMETER(S):
9114  * [I] infoPtr : valid pointer to the listview structure
9115  * [I] nScrollCode : scroll code
9116  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9117  * [I] hScrollWnd  : scrollbar control window handle
9118  *
9119  * RETURN:
9120  * Zero
9121  *
9122  * NOTES:
9123  *   SB_LINELEFT/SB_LINERIGHT:
9124  *        for LVS_ICON, LVS_SMALLICON  1 pixel
9125  *        for LVS_REPORT is 1 pixel
9126  *        for LVS_LIST  is 1 column --> which is a 1 because the
9127  *                                      scroll is based on columns not pixels
9128  *
9129  */
9130 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9131                                 INT nScrollDiff, HWND hScrollWnd)
9132 {
9133     INT nOldScrollPos, nNewScrollPos;
9134     SCROLLINFO scrollInfo;
9135
9136     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
9137         debugscrollcode(nScrollCode), nScrollDiff);
9138
9139     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9140
9141     scrollInfo.cbSize = sizeof(SCROLLINFO);
9142     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9143
9144     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
9145
9146     nOldScrollPos = scrollInfo.nPos;
9147
9148     switch (nScrollCode)
9149     {
9150     case SB_INTERNAL:
9151         break;
9152
9153     case SB_LINELEFT:
9154         nScrollDiff = -1;
9155         break;
9156
9157     case SB_LINERIGHT:
9158         nScrollDiff = 1;
9159         break;
9160
9161     case SB_PAGELEFT:
9162         nScrollDiff = -scrollInfo.nPage;
9163         break;
9164
9165     case SB_PAGERIGHT:
9166         nScrollDiff = scrollInfo.nPage;
9167         break;
9168
9169     case SB_THUMBPOSITION:
9170     case SB_THUMBTRACK:
9171         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9172         break;
9173
9174     default:
9175         nScrollDiff = 0;
9176     }
9177
9178     /* quit right away if pos isn't changing */
9179     if (nScrollDiff == 0) return 0;
9180     
9181     /* calculate new position, and handle overflows */
9182     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9183     if (nScrollDiff > 0) {
9184         if (nNewScrollPos < nOldScrollPos ||
9185             nNewScrollPos > scrollInfo.nMax)
9186             nNewScrollPos = scrollInfo.nMax;
9187     } else {
9188         if (nNewScrollPos > nOldScrollPos ||
9189             nNewScrollPos < scrollInfo.nMin)
9190             nNewScrollPos = scrollInfo.nMin;
9191     }
9192
9193     /* set the new position, and reread in case it changed */
9194     scrollInfo.fMask = SIF_POS;
9195     scrollInfo.nPos = nNewScrollPos;
9196     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
9197     
9198     /* carry on only if it really changed */
9199     if (nNewScrollPos == nOldScrollPos) return 0;
9200     
9201     if (infoPtr->uView == LV_VIEW_DETAILS)
9202         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
9203       
9204     /* now adjust to client coordinates */
9205     nScrollDiff = nOldScrollPos - nNewScrollPos;
9206     if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
9207    
9208     /* and scroll the window */
9209     scroll_list(infoPtr, nScrollDiff, 0);
9210
9211   return 0;
9212 }
9213
9214 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
9215 {
9216     INT gcWheelDelta = 0;
9217     INT pulScrollLines = 3;
9218     SCROLLINFO scrollInfo;
9219
9220     TRACE("(wheelDelta=%d)\n", wheelDelta);
9221
9222     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
9223     gcWheelDelta -= wheelDelta;
9224
9225     scrollInfo.cbSize = sizeof(SCROLLINFO);
9226     scrollInfo.fMask = SIF_POS;
9227
9228     switch(infoPtr->uView)
9229     {
9230     case LV_VIEW_ICON:
9231     case LV_VIEW_SMALLICON:
9232        /*
9233         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9234         *  should be fixed in the future.
9235         */
9236         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
9237                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
9238         break;
9239
9240     case LV_VIEW_DETAILS:
9241         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
9242         {
9243             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
9244             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
9245             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
9246         }
9247         break;
9248
9249     case LV_VIEW_LIST:
9250         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
9251         break;
9252     }
9253     return 0;
9254 }
9255
9256 /***
9257  * DESCRIPTION:
9258  * ???
9259  *
9260  * PARAMETER(S):
9261  * [I] infoPtr : valid pointer to the listview structure
9262  * [I] nVirtualKey : virtual key
9263  * [I] lKeyData : key data
9264  *
9265  * RETURN:
9266  * Zero
9267  */
9268 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
9269 {
9270   HWND hwndSelf = infoPtr->hwndSelf;
9271   INT nItem = -1;
9272   NMLVKEYDOWN nmKeyDown;
9273
9274   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
9275
9276   /* send LVN_KEYDOWN notification */
9277   nmKeyDown.wVKey = nVirtualKey;
9278   nmKeyDown.flags = 0;
9279   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
9280   if (!IsWindow(hwndSelf))
9281     return 0;
9282
9283   switch (nVirtualKey)
9284   {
9285   case VK_SPACE:
9286     nItem = infoPtr->nFocusedItem;
9287     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
9288         toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
9289     break;
9290
9291   case VK_RETURN:
9292     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
9293     {
9294         if (!notify(infoPtr, NM_RETURN)) return 0;
9295         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
9296     }
9297     break;
9298
9299   case VK_HOME:
9300     if (infoPtr->nItemCount > 0)
9301       nItem = 0;
9302     break;
9303
9304   case VK_END:
9305     if (infoPtr->nItemCount > 0)
9306       nItem = infoPtr->nItemCount - 1;
9307     break;
9308
9309   case VK_LEFT:
9310     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
9311     break;
9312
9313   case VK_UP:
9314     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
9315     break;
9316
9317   case VK_RIGHT:
9318     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
9319     break;
9320
9321   case VK_DOWN:
9322     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
9323     break;
9324
9325   case VK_PRIOR:
9326     if (infoPtr->uView == LV_VIEW_DETAILS)
9327     {
9328       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9329       if (infoPtr->nFocusedItem == topidx)
9330         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
9331       else
9332         nItem = topidx;
9333     }
9334     else
9335       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
9336                                     * LISTVIEW_GetCountPerRow(infoPtr);
9337     if(nItem < 0) nItem = 0;
9338     break;
9339
9340   case VK_NEXT:
9341     if (infoPtr->uView == LV_VIEW_DETAILS)
9342     {
9343       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9344       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
9345       if (infoPtr->nFocusedItem == topidx + cnt - 1)
9346         nItem = infoPtr->nFocusedItem + cnt - 1;
9347       else
9348         nItem = topidx + cnt - 1;
9349     }
9350     else
9351       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
9352                                     * LISTVIEW_GetCountPerRow(infoPtr);
9353     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
9354     break;
9355   }
9356
9357   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
9358       LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
9359
9360   return 0;
9361 }
9362
9363 /***
9364  * DESCRIPTION:
9365  * Kills the focus.
9366  *
9367  * PARAMETER(S):
9368  * [I] infoPtr : valid pointer to the listview structure
9369  *
9370  * RETURN:
9371  * Zero
9372  */
9373 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
9374 {
9375     TRACE("()\n");
9376
9377     /* if we did not have the focus, there's nothing to do */
9378     if (!infoPtr->bFocus) return 0;
9379    
9380     /* send NM_KILLFOCUS notification */
9381     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
9382
9383     /* if we have a focus rectangle, get rid of it */
9384     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
9385     
9386     /* set window focus flag */
9387     infoPtr->bFocus = FALSE;
9388
9389     /* invalidate the selected items before resetting focus flag */
9390     LISTVIEW_InvalidateSelectedItems(infoPtr);
9391     
9392     return 0;
9393 }
9394
9395 /***
9396  * DESCRIPTION:
9397  * Processes double click messages (left mouse button).
9398  *
9399  * PARAMETER(S):
9400  * [I] infoPtr : valid pointer to the listview structure
9401  * [I] wKey : key flag
9402  * [I] x,y : mouse coordinate
9403  *
9404  * RETURN:
9405  * Zero
9406  */
9407 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9408 {
9409     LVHITTESTINFO htInfo;
9410
9411     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9412     
9413     /* Cancel the item edition if any */
9414     if (infoPtr->itemEdit.fEnabled)
9415     {
9416       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
9417       infoPtr->itemEdit.fEnabled = FALSE;
9418     }
9419
9420     /* send NM_RELEASEDCAPTURE notification */
9421     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9422
9423     htInfo.pt.x = x;
9424     htInfo.pt.y = y;
9425
9426     /* send NM_DBLCLK notification */
9427     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
9428     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
9429
9430     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
9431     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
9432
9433     return 0;
9434 }
9435
9436 /***
9437  * DESCRIPTION:
9438  * Processes mouse down messages (left mouse button).
9439  *
9440  * PARAMETERS:
9441  *   infoPtr  [I ] valid pointer to the listview structure
9442  *   wKey     [I ] key flag
9443  *   x,y      [I ] mouse coordinate
9444  *
9445  * RETURN:
9446  *   Zero
9447  */
9448 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9449 {
9450   LVHITTESTINFO lvHitTestInfo;
9451   static BOOL bGroupSelect = TRUE;
9452   POINT pt = { x, y };
9453   INT nItem;
9454
9455   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9456
9457   /* send NM_RELEASEDCAPTURE notification */
9458   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9459
9460   /* set left button down flag and record the click position */
9461   infoPtr->bLButtonDown = TRUE;
9462   infoPtr->ptClickPos = pt;
9463   infoPtr->bDragging = FALSE;
9464   infoPtr->bMarqueeSelect = FALSE;
9465
9466   lvHitTestInfo.pt.x = x;
9467   lvHitTestInfo.pt.y = y;
9468
9469   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9470   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
9471   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9472   {
9473     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
9474     {
9475         toggle_checkbox_state(infoPtr, nItem);
9476         return 0;
9477     }
9478
9479     if (infoPtr->dwStyle & LVS_SINGLESEL)
9480     {
9481       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9482         infoPtr->nEditLabelItem = nItem;
9483       else
9484         LISTVIEW_SetSelection(infoPtr, nItem);
9485     }
9486     else
9487     {
9488       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9489       {
9490         if (bGroupSelect)
9491         {
9492           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9493           LISTVIEW_SetItemFocus(infoPtr, nItem);
9494           infoPtr->nSelectionMark = nItem;
9495         }
9496         else
9497         {
9498           LVITEMW item;
9499
9500           item.state = LVIS_SELECTED | LVIS_FOCUSED;
9501           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9502
9503           LISTVIEW_SetItemState(infoPtr,nItem,&item);
9504           infoPtr->nSelectionMark = nItem;
9505         }
9506       }
9507       else if (wKey & MK_CONTROL)
9508       {
9509         LVITEMW item;
9510
9511         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9512         
9513         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9514         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9515         LISTVIEW_SetItemState(infoPtr, nItem, &item);
9516         infoPtr->nSelectionMark = nItem;
9517       }
9518       else  if (wKey & MK_SHIFT)
9519       {
9520         LISTVIEW_SetGroupSelection(infoPtr, nItem);
9521       }
9522       else
9523       {
9524         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9525         {
9526           infoPtr->nEditLabelItem = nItem;
9527           infoPtr->nLButtonDownItem = nItem;
9528
9529           LISTVIEW_SetItemFocus(infoPtr, nItem);
9530         }
9531         else
9532           /* set selection (clears other pre-existing selections) */
9533           LISTVIEW_SetSelection(infoPtr, nItem);
9534       }
9535     }
9536
9537     if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9538         if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9539   }
9540   else
9541   {
9542     /* remove all selections */
9543     if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9544         LISTVIEW_DeselectAll(infoPtr);
9545     ReleaseCapture();
9546   }
9547   
9548   return 0;
9549 }
9550
9551 /***
9552  * DESCRIPTION:
9553  * Processes mouse up messages (left mouse button).
9554  *
9555  * PARAMETERS:
9556  *   infoPtr [I ] valid pointer to the listview structure
9557  *   wKey    [I ] key flag
9558  *   x,y     [I ] mouse coordinate
9559  *
9560  * RETURN:
9561  *   Zero
9562  */
9563 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9564 {
9565     LVHITTESTINFO lvHitTestInfo;
9566     
9567     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9568
9569     if (!infoPtr->bLButtonDown) return 0;
9570
9571     lvHitTestInfo.pt.x = x;
9572     lvHitTestInfo.pt.y = y;
9573
9574     /* send NM_CLICK notification */
9575     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9576     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9577
9578     /* set left button flag */
9579     infoPtr->bLButtonDown = FALSE;
9580
9581     /* set a single selection, reset others */
9582     if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9583         LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9584     infoPtr->nLButtonDownItem = -1;
9585
9586     if (infoPtr->bDragging || infoPtr->bMarqueeSelect)
9587     {
9588         /* Remove the marquee rectangle and release our mouse capture */
9589         if (infoPtr->bMarqueeSelect)
9590         {
9591             LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
9592             ReleaseCapture();
9593         }
9594
9595         SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
9596
9597         infoPtr->bDragging = FALSE;
9598         infoPtr->bMarqueeSelect = FALSE;
9599         return 0;
9600     }
9601
9602     /* if we clicked on a selected item, edit the label */
9603     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9604     {
9605         /* we want to make sure the user doesn't want to do a double click. So we will
9606          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9607          */
9608         infoPtr->itemEdit.fEnabled = TRUE;
9609         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9610         SetTimer(infoPtr->hwndSelf,
9611             (UINT_PTR)&infoPtr->itemEdit,
9612             GetDoubleClickTime(),
9613             LISTVIEW_DelayedEditItem);
9614     }
9615
9616     if (!infoPtr->bFocus)
9617         SetFocus(infoPtr->hwndSelf);
9618
9619     return 0;
9620 }
9621
9622 /***
9623  * DESCRIPTION:
9624  * Destroys the listview control (called after WM_DESTROY).
9625  *
9626  * PARAMETER(S):
9627  * [I] infoPtr : valid pointer to the listview structure
9628  *
9629  * RETURN:
9630  * Zero
9631  */
9632 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9633 {
9634   TRACE("()\n");
9635
9636   /* delete all items */
9637   LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9638
9639   /* destroy data structure */
9640   DPA_Destroy(infoPtr->hdpaItems);
9641   DPA_Destroy(infoPtr->hdpaItemIds);
9642   DPA_Destroy(infoPtr->hdpaPosX);
9643   DPA_Destroy(infoPtr->hdpaPosY);
9644   DPA_Destroy(infoPtr->hdpaColumns);
9645   ranges_destroy(infoPtr->selectionRanges);
9646
9647   /* destroy image lists */
9648   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9649   {
9650       if (infoPtr->himlNormal)
9651           ImageList_Destroy(infoPtr->himlNormal);
9652       if (infoPtr->himlSmall)
9653           ImageList_Destroy(infoPtr->himlSmall);
9654       if (infoPtr->himlState)
9655           ImageList_Destroy(infoPtr->himlState);
9656   }
9657
9658   /* destroy font, bkgnd brush */
9659   infoPtr->hFont = 0;
9660   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9661   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9662
9663   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9664
9665   /* free listview info pointer*/
9666   Free(infoPtr);
9667
9668   return 0;
9669 }
9670
9671 /***
9672  * DESCRIPTION:
9673  * Handles notifications from header.
9674  *
9675  * PARAMETER(S):
9676  * [I] infoPtr : valid pointer to the listview structure
9677  * [I] nCtrlId : control identifier
9678  * [I] lpnmh : notification information
9679  *
9680  * RETURN:
9681  * Zero
9682  */
9683 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9684 {
9685     HWND hwndSelf = infoPtr->hwndSelf;
9686     
9687     TRACE("(lpnmh=%p)\n", lpnmh);
9688
9689     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9690     
9691     switch (lpnmh->hdr.code)
9692     {    
9693         case HDN_TRACKW:
9694         case HDN_TRACKA:
9695         {
9696             COLUMN_INFO *lpColumnInfo;
9697             POINT ptOrigin;
9698             INT x;
9699             
9700             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9701                 break;
9702
9703             /* remove the old line (if any) */
9704             LISTVIEW_DrawTrackLine(infoPtr);
9705             
9706             /* compute & draw the new line */
9707             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9708             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9709             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9710             infoPtr->xTrackLine = x + ptOrigin.x;
9711             LISTVIEW_DrawTrackLine(infoPtr);
9712             break;
9713         }
9714         
9715         case HDN_ENDTRACKA:
9716         case HDN_ENDTRACKW:
9717             /* remove the track line (if any) */
9718             LISTVIEW_DrawTrackLine(infoPtr);
9719             infoPtr->xTrackLine = -1;
9720             break;
9721
9722         case HDN_BEGINDRAG:
9723             notify_forward_header(infoPtr, lpnmh);
9724             return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
9725
9726         case HDN_ENDDRAG:
9727             infoPtr->colRectsDirty = TRUE;
9728             LISTVIEW_InvalidateList(infoPtr);
9729             notify_forward_header(infoPtr, lpnmh);
9730             return FALSE;
9731
9732         case HDN_ITEMCHANGINGW:
9733         case HDN_ITEMCHANGINGA:
9734             return notify_forward_header(infoPtr, lpnmh);
9735             
9736         case HDN_ITEMCHANGEDW:
9737         case HDN_ITEMCHANGEDA:
9738         {
9739             COLUMN_INFO *lpColumnInfo;
9740             HDITEMW hdi;
9741             INT dx, cxy;
9742             
9743             notify_forward_header(infoPtr, lpnmh);
9744             if (!IsWindow(hwndSelf))
9745                 break;
9746
9747             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9748             {
9749                 hdi.mask = HDI_WIDTH;
9750                 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
9751                 cxy = hdi.cxy;
9752             }
9753             else
9754                 cxy = lpnmh->pitem->cxy;
9755             
9756             /* determine how much we change since the last know position */
9757             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9758             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9759             if (dx != 0)
9760             {
9761                 lpColumnInfo->rcHeader.right += dx;
9762
9763                 hdi.mask = HDI_ORDER;
9764                 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi);
9765
9766                 /* not the rightmost one */
9767                 if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9768                 {
9769                     INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
9770                                               hdi.iOrder + 1, 0);
9771                     LISTVIEW_ScrollColumns(infoPtr, nIndex, dx);
9772                 }
9773                 else
9774                 {
9775                     /* only needs to update the scrolls */
9776                     infoPtr->nItemWidth += dx;
9777                     LISTVIEW_UpdateScroll(infoPtr);
9778                 }
9779                 LISTVIEW_UpdateItemSize(infoPtr);
9780                 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
9781                 {
9782                     POINT ptOrigin;
9783                     RECT rcCol = lpColumnInfo->rcHeader;
9784                     
9785                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9786                     OffsetRect(&rcCol, ptOrigin.x, 0);
9787                     
9788                     rcCol.top = infoPtr->rcList.top;
9789                     rcCol.bottom = infoPtr->rcList.bottom;
9790
9791                     /* resizing left-aligned columns leaves most of the left side untouched */
9792                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9793                     {
9794                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9795                         if (dx > 0)
9796                             nMaxDirty += dx;
9797                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9798                     }
9799
9800                     /* when shrinking the last column clear the now unused field */
9801                     if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9802                     {
9803                         RECT right;
9804
9805                         rcCol.right -= dx;
9806
9807                         /* deal with right from rightmost column area */
9808                         right.left = rcCol.right;
9809                         right.top  = rcCol.top;
9810                         right.bottom = rcCol.bottom;
9811                         right.right = infoPtr->rcList.right;
9812
9813                         LISTVIEW_InvalidateRect(infoPtr, &right);
9814                     }
9815
9816                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9817                 }
9818             }
9819         }
9820         break;
9821
9822         case HDN_ITEMCLICKW:
9823         case HDN_ITEMCLICKA:
9824         {
9825             /* Handle sorting by Header Column */
9826             NMLISTVIEW nmlv;
9827
9828             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9829             nmlv.iItem = -1;
9830             nmlv.iSubItem = lpnmh->iItem;
9831             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9832             notify_forward_header(infoPtr, lpnmh);
9833         }
9834         break;
9835
9836         case HDN_DIVIDERDBLCLICKW:
9837         case HDN_DIVIDERDBLCLICKA:
9838             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9839             notify_forward_header(infoPtr, lpnmh);
9840             break;
9841     }
9842
9843     return 0;
9844 }
9845
9846 /***
9847  * DESCRIPTION:
9848  * Paint non-client area of control.
9849  *
9850  * PARAMETER(S):
9851  * [I] infoPtr : valid pointer to the listview structureof the sender
9852  * [I] region : update region
9853  *
9854  * RETURN:
9855  *  TRUE  - frame was painted
9856  *  FALSE - call default window proc
9857  */
9858 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9859 {
9860     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9861     HDC dc;
9862     RECT r;
9863     HRGN cliprgn;
9864     int cxEdge = GetSystemMetrics (SM_CXEDGE),
9865         cyEdge = GetSystemMetrics (SM_CYEDGE);
9866
9867     if (!theme) return FALSE;
9868
9869     GetWindowRect(infoPtr->hwndSelf, &r);
9870
9871     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9872         r.right - cxEdge, r.bottom - cyEdge);
9873     if (region != (HRGN)1)
9874         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9875     OffsetRect(&r, -r.left, -r.top);
9876
9877     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9878     OffsetRect(&r, -r.left, -r.top);
9879
9880     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9881         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9882     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9883     ReleaseDC(infoPtr->hwndSelf, dc);
9884
9885     /* Call default proc to get the scrollbars etc. painted */
9886     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9887
9888     return TRUE;
9889 }
9890
9891 /***
9892  * DESCRIPTION:
9893  * Determines the type of structure to use.
9894  *
9895  * PARAMETER(S):
9896  * [I] infoPtr : valid pointer to the listview structureof the sender
9897  * [I] hwndFrom : listview window handle
9898  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9899  *
9900  * RETURN:
9901  * Zero
9902  */
9903 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9904 {
9905     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9906
9907     if (nCommand == NF_REQUERY)
9908         infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9909
9910     return infoPtr->notifyFormat;
9911 }
9912
9913 /***
9914  * DESCRIPTION:
9915  * Paints/Repaints the listview control. Internal use.
9916  *
9917  * PARAMETER(S):
9918  * [I] infoPtr : valid pointer to the listview structure
9919  * [I] hdc : device context handle
9920  *
9921  * RETURN:
9922  * Zero
9923  */
9924 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9925 {
9926     TRACE("(hdc=%p)\n", hdc);
9927
9928     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9929     {
9930         infoPtr->bNoItemMetrics = FALSE;
9931         LISTVIEW_UpdateItemSize(infoPtr);
9932         if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
9933             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9934         LISTVIEW_UpdateScroll(infoPtr);
9935     }
9936
9937     if (infoPtr->hwndHeader)  UpdateWindow(infoPtr->hwndHeader);
9938
9939     if (hdc) 
9940         LISTVIEW_Refresh(infoPtr, hdc, NULL);
9941     else
9942     {
9943         PAINTSTRUCT ps;
9944
9945         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9946         if (!hdc) return 1;
9947         LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9948         EndPaint(infoPtr->hwndSelf, &ps);
9949     }
9950
9951     return 0;
9952 }
9953
9954 /***
9955  * DESCRIPTION:
9956  * Paints/Repaints the listview control, WM_PAINT handler.
9957  *
9958  * PARAMETER(S):
9959  * [I] infoPtr : valid pointer to the listview structure
9960  * [I] hdc : device context handle
9961  *
9962  * RETURN:
9963  * Zero
9964  */
9965 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc)
9966 {
9967     TRACE("(hdc=%p)\n", hdc);
9968
9969     if (!is_redrawing(infoPtr))
9970         return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0);
9971
9972     return LISTVIEW_Paint(infoPtr, hdc);
9973 }
9974
9975 /***
9976  * DESCRIPTION:
9977  * Paints/Repaints the listview control.
9978  *
9979  * PARAMETER(S):
9980  * [I] infoPtr : valid pointer to the listview structure
9981  * [I] hdc : device context handle
9982  * [I] options : drawing options
9983  *
9984  * RETURN:
9985  * Zero
9986  */
9987 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9988 {
9989     FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9990
9991     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9992         return 0;
9993
9994     if (options & PRF_ERASEBKGND)
9995         LISTVIEW_EraseBkgnd(infoPtr, hdc);
9996
9997     if (options & PRF_CLIENT)
9998         LISTVIEW_Paint(infoPtr, hdc);
9999
10000     return 0;
10001 }
10002
10003
10004 /***
10005  * DESCRIPTION:
10006  * Processes double click messages (right mouse button).
10007  *
10008  * PARAMETER(S):
10009  * [I] infoPtr : valid pointer to the listview structure
10010  * [I] wKey : key flag
10011  * [I] x,y : mouse coordinate
10012  *
10013  * RETURN:
10014  * Zero
10015  */
10016 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10017 {
10018     LVHITTESTINFO lvHitTestInfo;
10019     
10020     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10021
10022     /* send NM_RELEASEDCAPTURE notification */
10023     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10024
10025     /* send NM_RDBLCLK notification */
10026     lvHitTestInfo.pt.x = x;
10027     lvHitTestInfo.pt.y = y;
10028     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10029     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
10030
10031     return 0;
10032 }
10033
10034 /***
10035  * DESCRIPTION:
10036  * Processes mouse down messages (right mouse button).
10037  *
10038  * PARAMETER(S):
10039  * [I] infoPtr : valid pointer to the listview structure
10040  * [I] wKey : key flag
10041  * [I] x,y : mouse coordinate
10042  *
10043  * RETURN:
10044  * Zero
10045  */
10046 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10047 {
10048     LVHITTESTINFO lvHitTestInfo;
10049     INT nItem;
10050
10051     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10052
10053     /* send NM_RELEASEDCAPTURE notification */
10054     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10055
10056     /* make sure the listview control window has the focus */
10057     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
10058
10059     /* set right button down flag */
10060     infoPtr->bRButtonDown = TRUE;
10061
10062     /* determine the index of the selected item */
10063     lvHitTestInfo.pt.x = x;
10064     lvHitTestInfo.pt.y = y;
10065     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
10066   
10067     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
10068     {
10069         LISTVIEW_SetItemFocus(infoPtr, nItem);
10070         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
10071             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10072             LISTVIEW_SetSelection(infoPtr, nItem);
10073     }
10074     else
10075     {
10076         LISTVIEW_DeselectAll(infoPtr);
10077     }
10078
10079     return 0;
10080 }
10081
10082 /***
10083  * DESCRIPTION:
10084  * Processes mouse up messages (right mouse button).
10085  *
10086  * PARAMETER(S):
10087  * [I] infoPtr : valid pointer to the listview structure
10088  * [I] wKey : key flag
10089  * [I] x,y : mouse coordinate
10090  *
10091  * RETURN:
10092  * Zero
10093  */
10094 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10095 {
10096     LVHITTESTINFO lvHitTestInfo;
10097     POINT pt;
10098
10099     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10100
10101     if (!infoPtr->bRButtonDown) return 0;
10102  
10103     /* set button flag */
10104     infoPtr->bRButtonDown = FALSE;
10105
10106     /* Send NM_RCLICK notification */
10107     lvHitTestInfo.pt.x = x;
10108     lvHitTestInfo.pt.y = y;
10109     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10110     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
10111
10112     /* Change to screen coordinate for WM_CONTEXTMENU */
10113     pt = lvHitTestInfo.pt;
10114     ClientToScreen(infoPtr->hwndSelf, &pt);
10115
10116     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
10117     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
10118                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
10119
10120     return 0;
10121 }
10122
10123
10124 /***
10125  * DESCRIPTION:
10126  * Sets the cursor.
10127  *
10128  * PARAMETER(S):
10129  * [I] infoPtr : valid pointer to the listview structure
10130  * [I] hwnd : window handle of window containing the cursor
10131  * [I] nHittest : hit-test code
10132  * [I] wMouseMsg : ideintifier of the mouse message
10133  *
10134  * RETURN:
10135  * TRUE if cursor is set
10136  * FALSE otherwise
10137  */
10138 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
10139 {
10140     LVHITTESTINFO lvHitTestInfo;
10141
10142     if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
10143
10144     if(!infoPtr->hHotCursor)  return FALSE;
10145
10146     GetCursorPos(&lvHitTestInfo.pt);
10147     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
10148
10149     SetCursor(infoPtr->hHotCursor);
10150
10151     return TRUE;
10152 }
10153
10154 /***
10155  * DESCRIPTION:
10156  * Sets the focus.
10157  *
10158  * PARAMETER(S):
10159  * [I] infoPtr : valid pointer to the listview structure
10160  * [I] hwndLoseFocus : handle of previously focused window
10161  *
10162  * RETURN:
10163  * Zero
10164  */
10165 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
10166 {
10167     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
10168
10169     /* if we have the focus already, there's nothing to do */
10170     if (infoPtr->bFocus) return 0;
10171    
10172     /* send NM_SETFOCUS notification */
10173     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
10174
10175     /* set window focus flag */
10176     infoPtr->bFocus = TRUE;
10177
10178     /* put the focus rect back on */
10179     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
10180
10181     /* redraw all visible selected items */
10182     LISTVIEW_InvalidateSelectedItems(infoPtr);
10183
10184     return 0;
10185 }
10186
10187 /***
10188  * DESCRIPTION:
10189  * Sets the font.
10190  *
10191  * PARAMETER(S):
10192  * [I] infoPtr : valid pointer to the listview structure
10193  * [I] fRedraw : font handle
10194  * [I] fRedraw : redraw flag
10195  *
10196  * RETURN:
10197  * Zero
10198  */
10199 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
10200 {
10201     HFONT oldFont = infoPtr->hFont;
10202
10203     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
10204
10205     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
10206     if (infoPtr->hFont == oldFont) return 0;
10207     
10208     LISTVIEW_SaveTextMetrics(infoPtr);
10209
10210     if (infoPtr->uView == LV_VIEW_DETAILS)
10211     {
10212         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
10213         LISTVIEW_UpdateSize(infoPtr);
10214         LISTVIEW_UpdateScroll(infoPtr);
10215     }
10216
10217     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
10218
10219     return 0;
10220 }
10221
10222 /***
10223  * DESCRIPTION:
10224  * Message handling for WM_SETREDRAW.
10225  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10226  *
10227  * PARAMETER(S):
10228  * [I] infoPtr : valid pointer to the listview structure
10229  * [I] bRedraw: state of redraw flag
10230  *
10231  * RETURN:
10232  * DefWinProc return value
10233  */
10234 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
10235 {
10236     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
10237
10238     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
10239     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
10240
10241     infoPtr->bRedraw = bRedraw;
10242
10243     if(!bRedraw) return 0;
10244     
10245     if (is_autoarrange(infoPtr))
10246         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10247     LISTVIEW_UpdateScroll(infoPtr);
10248
10249     /* despite what the WM_SETREDRAW docs says, apps expect us
10250      * to invalidate the listview here... stupid! */
10251     LISTVIEW_InvalidateList(infoPtr);
10252
10253     return 0;
10254 }
10255
10256 /***
10257  * DESCRIPTION:
10258  * Resizes the listview control. This function processes WM_SIZE
10259  * messages.  At this time, the width and height are not used.
10260  *
10261  * PARAMETER(S):
10262  * [I] infoPtr : valid pointer to the listview structure
10263  * [I] Width : new width
10264  * [I] Height : new height
10265  *
10266  * RETURN:
10267  * Zero
10268  */
10269 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
10270 {
10271     RECT rcOld = infoPtr->rcList;
10272
10273     TRACE("(width=%d, height=%d)\n", Width, Height);
10274
10275     LISTVIEW_UpdateSize(infoPtr);
10276     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
10277   
10278     /* do not bother with display related stuff if we're not redrawing */ 
10279     if (!is_redrawing(infoPtr)) return 0;
10280     
10281     if (is_autoarrange(infoPtr)) 
10282         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10283
10284     LISTVIEW_UpdateScroll(infoPtr);
10285
10286     /* refresh all only for lists whose height changed significantly */
10287     if ((infoPtr->uView == LV_VIEW_LIST) &&
10288         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
10289         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
10290         LISTVIEW_InvalidateList(infoPtr);
10291
10292   return 0;
10293 }
10294
10295 /***
10296  * DESCRIPTION:
10297  * Sets the size information.
10298  *
10299  * PARAMETER(S):
10300  * [I] infoPtr : valid pointer to the listview structure
10301  *
10302  * RETURN:
10303  *  None
10304  */
10305 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
10306 {
10307     TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
10308     
10309     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
10310
10311     if (infoPtr->uView == LV_VIEW_LIST)
10312     {
10313         /* Apparently the "LIST" style is supposed to have the same
10314          * number of items in a column even if there is no scroll bar.
10315          * Since if a scroll bar already exists then the bottom is already
10316          * reduced, only reduce if the scroll bar does not currently exist.
10317          * The "2" is there to mimic the native control. I think it may be
10318          * related to either padding or edges.  (GLA 7/2002)
10319          */
10320         if (!(infoPtr->dwStyle & WS_HSCROLL))
10321             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
10322         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
10323     }
10324     else if (infoPtr->uView == LV_VIEW_DETAILS)
10325     {
10326         HDLAYOUT hl;
10327         WINDOWPOS wp;
10328
10329         hl.prc = &infoPtr->rcList;
10330         hl.pwpos = &wp;
10331         SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10332         TRACE("  wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
10333         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
10334                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10335                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10336         TRACE("  after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
10337
10338         infoPtr->rcList.top = max(wp.cy, 0);
10339         infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
10340     }
10341
10342     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
10343 }
10344
10345 /***
10346  * DESCRIPTION:
10347  * Processes WM_STYLECHANGED messages.
10348  *
10349  * PARAMETER(S):
10350  * [I] infoPtr : valid pointer to the listview structure
10351  * [I] wStyleType : window style type (normal or extended)
10352  * [I] lpss : window style information
10353  *
10354  * RETURN:
10355  * Zero
10356  */
10357 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10358                                  const STYLESTRUCT *lpss)
10359 {
10360     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
10361     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
10362     UINT style;
10363
10364     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10365           wStyleType, lpss->styleOld, lpss->styleNew);
10366
10367     if (wStyleType != GWL_STYLE) return 0;
10368
10369     infoPtr->dwStyle = lpss->styleNew;
10370     map_style_view(infoPtr);
10371
10372     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
10373         ((lpss->styleNew & WS_HSCROLL) == 0))
10374        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
10375
10376     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
10377         ((lpss->styleNew & WS_VSCROLL) == 0))
10378        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
10379
10380     if (uNewView != uOldView)
10381     {
10382         SIZE oldIconSize = infoPtr->iconSize;
10383         HIMAGELIST himl;
10384     
10385         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
10386         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
10387
10388         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
10389         SetRectEmpty(&infoPtr->rcFocus);
10390
10391         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
10392         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
10393     
10394         if (uNewView == LVS_ICON)
10395         {
10396             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
10397             {
10398                 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
10399                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
10400                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
10401             }
10402         }
10403         else if (uNewView == LVS_REPORT)
10404         {
10405             HDLAYOUT hl;
10406             WINDOWPOS wp;
10407
10408             LISTVIEW_CreateHeader( infoPtr );
10409
10410             hl.prc = &infoPtr->rcList;
10411             hl.pwpos = &wp;
10412             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10413             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
10414                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10415                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10416         }
10417
10418         LISTVIEW_UpdateItemSize(infoPtr);
10419     }
10420
10421     if (uNewView == LVS_REPORT)
10422     {
10423         if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
10424         {
10425             if (lpss->styleNew & LVS_NOCOLUMNHEADER)
10426             {
10427                 /* Turn off the header control */
10428                 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
10429                 TRACE("Hide header control, was 0x%08x\n", style);
10430                 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
10431             } else {
10432                 /* Turn on the header control */
10433                 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
10434                 {
10435                     TRACE("Show header control, was 0x%08x\n", style);
10436                     SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
10437                 }
10438             }
10439         }
10440     }
10441
10442     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
10443          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
10444          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10445
10446     /* update the size of the client area */
10447     LISTVIEW_UpdateSize(infoPtr);
10448
10449     /* add scrollbars if needed */
10450     LISTVIEW_UpdateScroll(infoPtr);
10451
10452     /* invalidate client area + erase background */
10453     LISTVIEW_InvalidateList(infoPtr);
10454
10455     return 0;
10456 }
10457
10458 /***
10459  * DESCRIPTION:
10460  * Processes WM_STYLECHANGING messages.
10461  *
10462  * PARAMETER(S):
10463  * [I] infoPtr : valid pointer to the listview structure
10464  * [I] wStyleType : window style type (normal or extended)
10465  * [I0] lpss : window style information
10466  *
10467  * RETURN:
10468  * Zero
10469  */
10470 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10471                                   STYLESTRUCT *lpss)
10472 {
10473     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10474           wStyleType, lpss->styleOld, lpss->styleNew);
10475
10476     /* don't forward LVS_OWNERDATA only if not already set to */
10477     if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
10478     {
10479         if (lpss->styleOld & LVS_OWNERDATA)
10480             lpss->styleNew |= LVS_OWNERDATA;
10481         else
10482             lpss->styleNew &= ~LVS_OWNERDATA;
10483     }
10484
10485     return 0;
10486 }
10487
10488 /***
10489  * DESCRIPTION:
10490  * Processes WM_SHOWWINDOW messages.
10491  *
10492  * PARAMETER(S):
10493  * [I] infoPtr : valid pointer to the listview structure
10494  * [I] bShown  : window is being shown (FALSE when hidden)
10495  * [I] iStatus : window show status
10496  *
10497  * RETURN:
10498  * Zero
10499  */
10500 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
10501 {
10502   /* header delayed creation */
10503   if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
10504   {
10505     LISTVIEW_CreateHeader(infoPtr);
10506
10507     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
10508       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
10509   }
10510
10511   return 0;
10512 }
10513
10514 /***
10515  * DESCRIPTION:
10516  * Processes CCM_GETVERSION messages.
10517  *
10518  * PARAMETER(S):
10519  * [I] infoPtr : valid pointer to the listview structure
10520  *
10521  * RETURN:
10522  * Current version
10523  */
10524 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
10525 {
10526   return infoPtr->iVersion;
10527 }
10528
10529 /***
10530  * DESCRIPTION:
10531  * Processes CCM_SETVERSION messages.
10532  *
10533  * PARAMETER(S):
10534  * [I] infoPtr  : valid pointer to the listview structure
10535  * [I] iVersion : version to be set
10536  *
10537  * RETURN:
10538  * -1 when requested version is greater than DLL version;
10539  * previous version otherwise
10540  */
10541 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10542 {
10543   INT iOldVersion = infoPtr->iVersion;
10544
10545   if (iVersion > COMCTL32_VERSION)
10546     return -1;
10547
10548   infoPtr->iVersion = iVersion;
10549
10550   TRACE("new version %d\n", iVersion);
10551
10552   return iOldVersion;
10553 }
10554
10555 /***
10556  * DESCRIPTION:
10557  * Window procedure of the listview control.
10558  *
10559  */
10560 static LRESULT WINAPI
10561 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10562 {
10563   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10564
10565   TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10566
10567   if (!infoPtr && (uMsg != WM_NCCREATE))
10568     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10569
10570   switch (uMsg)
10571   {
10572   case LVM_APPROXIMATEVIEWRECT:
10573     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10574                                         LOWORD(lParam), HIWORD(lParam));
10575   case LVM_ARRANGE:
10576     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10577
10578   case LVM_CANCELEDITLABEL:
10579     return LISTVIEW_CancelEditLabel(infoPtr);
10580
10581   case LVM_CREATEDRAGIMAGE:
10582     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10583
10584   case LVM_DELETEALLITEMS:
10585     return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10586
10587   case LVM_DELETECOLUMN:
10588     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10589
10590   case LVM_DELETEITEM:
10591     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10592
10593   case LVM_EDITLABELW:
10594     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
10595
10596   case LVM_EDITLABELA:
10597     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
10598
10599   /* case LVM_ENABLEGROUPVIEW: */
10600
10601   case LVM_ENSUREVISIBLE:
10602     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10603
10604   case LVM_FINDITEMW:
10605     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10606
10607   case LVM_FINDITEMA:
10608     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10609
10610   case LVM_GETBKCOLOR:
10611     return infoPtr->clrBk;
10612
10613   /* case LVM_GETBKIMAGE: */
10614
10615   case LVM_GETCALLBACKMASK:
10616     return infoPtr->uCallbackMask;
10617
10618   case LVM_GETCOLUMNA:
10619     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10620
10621   case LVM_GETCOLUMNW:
10622     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10623
10624   case LVM_GETCOLUMNORDERARRAY:
10625     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10626
10627   case LVM_GETCOLUMNWIDTH:
10628     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10629
10630   case LVM_GETCOUNTPERPAGE:
10631     return LISTVIEW_GetCountPerPage(infoPtr);
10632
10633   case LVM_GETEDITCONTROL:
10634     return (LRESULT)infoPtr->hwndEdit;
10635
10636   case LVM_GETEXTENDEDLISTVIEWSTYLE:
10637     return infoPtr->dwLvExStyle;
10638
10639   /* case LVM_GETGROUPINFO: */
10640
10641   /* case LVM_GETGROUPMETRICS: */
10642
10643   case LVM_GETHEADER:
10644     return (LRESULT)infoPtr->hwndHeader;
10645
10646   case LVM_GETHOTCURSOR:
10647     return (LRESULT)infoPtr->hHotCursor;
10648
10649   case LVM_GETHOTITEM:
10650     return infoPtr->nHotItem;
10651
10652   case LVM_GETHOVERTIME:
10653     return infoPtr->dwHoverTime;
10654
10655   case LVM_GETIMAGELIST:
10656     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10657
10658   /* case LVM_GETINSERTMARK: */
10659
10660   /* case LVM_GETINSERTMARKCOLOR: */
10661
10662   /* case LVM_GETINSERTMARKRECT: */
10663
10664   case LVM_GETISEARCHSTRINGA:
10665   case LVM_GETISEARCHSTRINGW:
10666     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10667     return FALSE;
10668
10669   case LVM_GETITEMA:
10670     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
10671
10672   case LVM_GETITEMW:
10673     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
10674
10675   case LVM_GETITEMCOUNT:
10676     return infoPtr->nItemCount;
10677
10678   case LVM_GETITEMPOSITION:
10679     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10680
10681   case LVM_GETITEMRECT:
10682     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10683
10684   case LVM_GETITEMSPACING:
10685     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10686
10687   case LVM_GETITEMSTATE:
10688     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10689
10690   case LVM_GETITEMTEXTA:
10691     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10692
10693   case LVM_GETITEMTEXTW:
10694     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10695
10696   case LVM_GETNEXTITEM:
10697     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10698
10699   case LVM_GETNUMBEROFWORKAREAS:
10700     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10701     return 1;
10702
10703   case LVM_GETORIGIN:
10704     if (!lParam) return FALSE;
10705     if (infoPtr->uView == LV_VIEW_DETAILS ||
10706         infoPtr->uView == LV_VIEW_LIST) return FALSE;
10707     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
10708     return TRUE;
10709
10710   /* case LVM_GETOUTLINECOLOR: */
10711
10712   /* case LVM_GETSELECTEDCOLUMN: */
10713
10714   case LVM_GETSELECTEDCOUNT:
10715     return LISTVIEW_GetSelectedCount(infoPtr);
10716
10717   case LVM_GETSELECTIONMARK:
10718     return infoPtr->nSelectionMark;
10719
10720   case LVM_GETSTRINGWIDTHA:
10721     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10722
10723   case LVM_GETSTRINGWIDTHW:
10724     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10725
10726   case LVM_GETSUBITEMRECT:
10727     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10728
10729   case LVM_GETTEXTBKCOLOR:
10730     return infoPtr->clrTextBk;
10731
10732   case LVM_GETTEXTCOLOR:
10733     return infoPtr->clrText;
10734
10735   /* case LVM_GETTILEINFO: */
10736
10737   /* case LVM_GETTILEVIEWINFO: */
10738
10739   case LVM_GETTOOLTIPS:
10740     if( !infoPtr->hwndToolTip )
10741         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10742     return (LRESULT)infoPtr->hwndToolTip;
10743
10744   case LVM_GETTOPINDEX:
10745     return LISTVIEW_GetTopIndex(infoPtr);
10746
10747   case LVM_GETUNICODEFORMAT:
10748     return (infoPtr->notifyFormat == NFR_UNICODE);
10749
10750   case LVM_GETVIEW:
10751     return infoPtr->uView;
10752
10753   case LVM_GETVIEWRECT:
10754     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10755
10756   case LVM_GETWORKAREAS:
10757     FIXME("LVM_GETWORKAREAS: unimplemented\n");
10758     return FALSE;
10759
10760   /* case LVM_HASGROUP: */
10761
10762   case LVM_HITTEST:
10763     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
10764
10765   case LVM_INSERTCOLUMNA:
10766     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10767
10768   case LVM_INSERTCOLUMNW:
10769     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10770
10771   /* case LVM_INSERTGROUP: */
10772
10773   /* case LVM_INSERTGROUPSORTED: */
10774
10775   case LVM_INSERTITEMA:
10776     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10777
10778   case LVM_INSERTITEMW:
10779     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10780
10781   /* case LVM_INSERTMARKHITTEST: */
10782
10783   /* case LVM_ISGROUPVIEWENABLED: */
10784
10785   case LVM_ISITEMVISIBLE:
10786     return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
10787
10788   case LVM_MAPIDTOINDEX:
10789     return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
10790
10791   case LVM_MAPINDEXTOID:
10792     return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
10793
10794   /* case LVM_MOVEGROUP: */
10795
10796   /* case LVM_MOVEITEMTOGROUP: */
10797
10798   case LVM_REDRAWITEMS:
10799     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10800
10801   /* case LVM_REMOVEALLGROUPS: */
10802
10803   /* case LVM_REMOVEGROUP: */
10804
10805   case LVM_SCROLL:
10806     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10807
10808   case LVM_SETBKCOLOR:
10809     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10810
10811   /* case LVM_SETBKIMAGE: */
10812
10813   case LVM_SETCALLBACKMASK:
10814     infoPtr->uCallbackMask = (UINT)wParam;
10815     return TRUE;
10816
10817   case LVM_SETCOLUMNA:
10818     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10819
10820   case LVM_SETCOLUMNW:
10821     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10822
10823   case LVM_SETCOLUMNORDERARRAY:
10824     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10825
10826   case LVM_SETCOLUMNWIDTH:
10827     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10828
10829   case LVM_SETEXTENDEDLISTVIEWSTYLE:
10830     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10831
10832   /* case LVM_SETGROUPINFO: */
10833
10834   /* case LVM_SETGROUPMETRICS: */
10835
10836   case LVM_SETHOTCURSOR:
10837     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10838
10839   case LVM_SETHOTITEM:
10840     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10841
10842   case LVM_SETHOVERTIME:
10843     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10844
10845   case LVM_SETICONSPACING:
10846     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10847
10848   case LVM_SETIMAGELIST:
10849     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10850
10851   /* case LVM_SETINFOTIP: */
10852
10853   /* case LVM_SETINSERTMARK: */
10854
10855   /* case LVM_SETINSERTMARKCOLOR: */
10856
10857   case LVM_SETITEMA:
10858   case LVM_SETITEMW:
10859     {
10860         if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10861         return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10862     }
10863
10864   case LVM_SETITEMCOUNT:
10865     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10866
10867   case LVM_SETITEMPOSITION:
10868     {
10869         POINT pt;
10870         pt.x = (short)LOWORD(lParam);
10871         pt.y = (short)HIWORD(lParam);
10872         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10873     }
10874
10875   case LVM_SETITEMPOSITION32:
10876     if (lParam == 0) return FALSE;
10877     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10878
10879   case LVM_SETITEMSTATE:
10880     if (lParam == 0) return FALSE;
10881     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10882
10883   case LVM_SETITEMTEXTA:
10884     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10885
10886   case LVM_SETITEMTEXTW:
10887     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10888
10889   /* case LVM_SETOUTLINECOLOR: */
10890
10891   /* case LVM_SETSELECTEDCOLUMN: */
10892
10893   case LVM_SETSELECTIONMARK:
10894     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10895
10896   case LVM_SETTEXTBKCOLOR:
10897     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10898
10899   case LVM_SETTEXTCOLOR:
10900     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10901
10902   /* case LVM_SETTILEINFO: */
10903
10904   /* case LVM_SETTILEVIEWINFO: */
10905
10906   /* case LVM_SETTILEWIDTH: */
10907
10908   case LVM_SETTOOLTIPS:
10909     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10910
10911   case LVM_SETUNICODEFORMAT:
10912     return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10913
10914   case LVM_SETVIEW:
10915     return LISTVIEW_SetView(infoPtr, wParam);
10916
10917   /* case LVM_SETWORKAREAS: */
10918
10919   /* case LVM_SORTGROUPS: */
10920
10921   case LVM_SORTITEMS:
10922     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10923
10924   case LVM_SORTITEMSEX:
10925     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10926
10927   case LVM_SUBITEMHITTEST:
10928     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10929
10930   case LVM_UPDATE:
10931     return LISTVIEW_Update(infoPtr, (INT)wParam);
10932
10933   case CCM_GETVERSION:
10934     return LISTVIEW_GetVersion(infoPtr);
10935
10936   case CCM_SETVERSION:
10937     return LISTVIEW_SetVersion(infoPtr, wParam);
10938
10939   case WM_CHAR:
10940     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10941
10942   case WM_COMMAND:
10943     return LISTVIEW_Command(infoPtr, wParam, lParam);
10944
10945   case WM_NCCREATE:
10946     return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10947
10948   case WM_CREATE:
10949     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10950
10951   case WM_DESTROY:
10952     return LISTVIEW_Destroy(infoPtr);
10953
10954   case WM_ENABLE:
10955     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10956
10957   case WM_ERASEBKGND:
10958     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10959
10960   case WM_GETDLGCODE:
10961     return DLGC_WANTCHARS | DLGC_WANTARROWS;
10962
10963   case WM_GETFONT:
10964     return (LRESULT)infoPtr->hFont;
10965
10966   case WM_HSCROLL:
10967     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10968
10969   case WM_KEYDOWN:
10970     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10971
10972   case WM_KILLFOCUS:
10973     return LISTVIEW_KillFocus(infoPtr);
10974
10975   case WM_LBUTTONDBLCLK:
10976     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10977
10978   case WM_LBUTTONDOWN:
10979     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10980
10981   case WM_LBUTTONUP:
10982     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10983
10984   case WM_MOUSEMOVE:
10985     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10986
10987   case WM_MOUSEHOVER:
10988     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10989
10990   case WM_NCDESTROY:
10991     return LISTVIEW_NCDestroy(infoPtr);
10992
10993   case WM_NCPAINT:
10994     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10995         return 0;
10996     goto fwd_msg;
10997
10998   case WM_NOTIFY:
10999     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
11000         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
11001     else return 0;
11002
11003   case WM_NOTIFYFORMAT:
11004     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
11005
11006   case WM_PRINTCLIENT:
11007     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
11008
11009   case WM_PAINT:
11010     return LISTVIEW_WMPaint(infoPtr, (HDC)wParam);
11011
11012   case WM_RBUTTONDBLCLK:
11013     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11014
11015   case WM_RBUTTONDOWN:
11016     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11017
11018   case WM_RBUTTONUP:
11019     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11020
11021   case WM_SETCURSOR:
11022     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
11023       return TRUE;
11024     goto fwd_msg;
11025
11026   case WM_SETFOCUS:
11027     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
11028
11029   case WM_SETFONT:
11030     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
11031
11032   case WM_SETREDRAW:
11033     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
11034
11035   case WM_SHOWWINDOW:
11036     LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
11037     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11038
11039   case WM_SIZE:
11040     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
11041
11042   case WM_STYLECHANGED:
11043     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11044
11045   case WM_STYLECHANGING:
11046     return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11047
11048   case WM_SYSCOLORCHANGE:
11049     COMCTL32_RefreshSysColors();
11050     return 0;
11051
11052 /*      case WM_TIMER: */
11053   case WM_THEMECHANGED:
11054     return LISTVIEW_ThemeChanged(infoPtr);
11055
11056   case WM_VSCROLL:
11057     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
11058
11059   case WM_MOUSEWHEEL:
11060       if (wParam & (MK_SHIFT | MK_CONTROL))
11061           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11062       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
11063
11064   case WM_WINDOWPOSCHANGED:
11065       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
11066       {
11067       SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
11068                    SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
11069
11070       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
11071       {
11072           if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
11073       }
11074
11075           LISTVIEW_UpdateSize(infoPtr);
11076           LISTVIEW_UpdateScroll(infoPtr);
11077       }
11078       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11079
11080 /*      case WM_WININICHANGE: */
11081
11082   default:
11083     if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
11084       ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
11085
11086   fwd_msg:
11087     /* call default window procedure */
11088     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11089   }
11090
11091 }
11092
11093 /***
11094  * DESCRIPTION:
11095  * Registers the window class.
11096  *
11097  * PARAMETER(S):
11098  * None
11099  *
11100  * RETURN:
11101  * None
11102  */
11103 void LISTVIEW_Register(void)
11104 {
11105     WNDCLASSW wndClass;
11106
11107     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
11108     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
11109     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
11110     wndClass.cbClsExtra = 0;
11111     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
11112     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
11113     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
11114     wndClass.lpszClassName = WC_LISTVIEWW;
11115     RegisterClassW(&wndClass);
11116 }
11117
11118 /***
11119  * DESCRIPTION:
11120  * Unregisters the window class.
11121  *
11122  * PARAMETER(S):
11123  * None
11124  *
11125  * RETURN:
11126  * None
11127  */
11128 void LISTVIEW_Unregister(void)
11129 {
11130     UnregisterClassW(WC_LISTVIEWW, NULL);
11131 }
11132
11133 /***
11134  * DESCRIPTION:
11135  * Handle any WM_COMMAND messages
11136  *
11137  * PARAMETER(S):
11138  * [I] infoPtr : valid pointer to the listview structure
11139  * [I] wParam : the first message parameter
11140  * [I] lParam : the second message parameter
11141  *
11142  * RETURN:
11143  *   Zero.
11144  */
11145 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
11146 {
11147     switch (HIWORD(wParam))
11148     {
11149         case EN_UPDATE:
11150         {
11151             /*
11152              * Adjust the edit window size
11153              */
11154             WCHAR buffer[1024];
11155             HDC           hdc = GetDC(infoPtr->hwndEdit);
11156             HFONT         hFont, hOldFont = 0;
11157             RECT          rect;
11158             SIZE          sz;
11159
11160             if (!infoPtr->hwndEdit || !hdc) return 0;
11161             GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
11162             GetWindowRect(infoPtr->hwndEdit, &rect);
11163
11164             /* Select font to get the right dimension of the string */
11165             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
11166             if(hFont != 0)
11167             {
11168                 hOldFont = SelectObject(hdc, hFont);
11169             }
11170
11171             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
11172             {
11173                 TEXTMETRICW textMetric;
11174
11175                 /* Add Extra spacing for the next character */
11176                 GetTextMetricsW(hdc, &textMetric);
11177                 sz.cx += (textMetric.tmMaxCharWidth * 2);
11178
11179                 SetWindowPos (
11180                     infoPtr->hwndEdit,
11181                     HWND_TOP,
11182                     0,
11183                     0,
11184                     sz.cx,
11185                     rect.bottom - rect.top,
11186                     SWP_DRAWFRAME|SWP_NOMOVE);
11187             }
11188             if(hFont != 0)
11189                 SelectObject(hdc, hOldFont);
11190
11191             ReleaseDC(infoPtr->hwndEdit, hdc);
11192
11193             break;
11194         }
11195         case EN_KILLFOCUS:
11196         {
11197             LISTVIEW_CancelEditLabel(infoPtr);
11198         }
11199
11200         default:
11201           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
11202     }
11203
11204     return 0;
11205 }
11206
11207
11208 /***
11209  * DESCRIPTION:
11210  * Subclassed edit control windproc function
11211  *
11212  * PARAMETER(S):
11213  * [I] hwnd : the edit window handle
11214  * [I] uMsg : the message that is to be processed
11215  * [I] wParam : first message parameter
11216  * [I] lParam : second message parameter
11217  * [I] isW : TRUE if input is Unicode
11218  *
11219  * RETURN:
11220  *   Zero.
11221  */
11222 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
11223 {
11224     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
11225     BOOL save = TRUE;
11226
11227     TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
11228           hwnd, uMsg, wParam, lParam, isW);
11229
11230     switch (uMsg)
11231     {
11232         case WM_GETDLGCODE:
11233           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
11234
11235         case WM_DESTROY:
11236         {
11237             WNDPROC editProc = infoPtr->EditWndProc;
11238             infoPtr->EditWndProc = 0;
11239             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
11240             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
11241         }
11242
11243         case WM_KEYDOWN:
11244             if (VK_ESCAPE == (INT)wParam)
11245             {
11246                 save = FALSE;
11247                 break;
11248             }
11249             else if (VK_RETURN == (INT)wParam)
11250                 break;
11251
11252         default:
11253             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
11254     }
11255
11256     /* kill the edit */
11257     if (infoPtr->hwndEdit)
11258         LISTVIEW_EndEditLabelT(infoPtr, save, isW);
11259
11260     SendMessageW(hwnd, WM_CLOSE, 0, 0);
11261     return 0;
11262 }
11263
11264 /***
11265  * DESCRIPTION:
11266  * Subclassed edit control Unicode windproc function
11267  *
11268  * PARAMETER(S):
11269  * [I] hwnd : the edit window handle
11270  * [I] uMsg : the message that is to be processed
11271  * [I] wParam : first message parameter
11272  * [I] lParam : second message parameter
11273  *
11274  * RETURN:
11275  */
11276 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11277 {
11278     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
11279 }
11280
11281 /***
11282  * DESCRIPTION:
11283  * Subclassed edit control ANSI windproc function
11284  *
11285  * PARAMETER(S):
11286  * [I] hwnd : the edit window handle
11287  * [I] uMsg : the message that is to be processed
11288  * [I] wParam : first message parameter
11289  * [I] lParam : second message parameter
11290  *
11291  * RETURN:
11292  */
11293 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11294 {
11295     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
11296 }
11297
11298 /***
11299  * DESCRIPTION:
11300  * Creates a subclassed edit control
11301  *
11302  * PARAMETER(S):
11303  * [I] infoPtr : valid pointer to the listview structure
11304  * [I] text : initial text for the edit
11305  * [I] style : the window style
11306  * [I] isW : TRUE if input is Unicode
11307  *
11308  * RETURN:
11309  */
11310 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
11311 {
11312     HWND hedit;
11313     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
11314
11315     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
11316
11317     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
11318
11319     /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
11320     if (isW)
11321         hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11322     else
11323         hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11324
11325     if (!hedit) return 0;
11326
11327     infoPtr->EditWndProc = (WNDPROC)
11328         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
11329                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
11330
11331     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
11332
11333     return hedit;
11334 }