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