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