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