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