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