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