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