comctl32: toolbar: The top margin should be computed and saved in CalcToolbar.
[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 = (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 = (RANGE *)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 = (RANGE *)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 = (RANGE *)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     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3510     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3511    
3512     /* get the subitem structure, and create it if not there */
3513     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3514     assert (hdpaSubItems);
3515     
3516     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3517     if (!lpSubItem)
3518     {
3519         SUBITEM_INFO *tmpSubItem;
3520         INT i;
3521
3522         lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3523         if (!lpSubItem) return FALSE;
3524         /* we could binary search here, if need be...*/
3525         for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3526         {
3527             tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3528             if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3529         }
3530         if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3531         {
3532             Free(lpSubItem);
3533             return FALSE;
3534         }
3535         lpSubItem->iSubItem = lpLVItem->iSubItem;
3536         lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3537         *bChanged = TRUE;
3538     }
3539     
3540     if (lpLVItem->mask & LVIF_IMAGE)
3541         if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3542         {
3543             lpSubItem->hdr.iImage = lpLVItem->iImage;
3544             *bChanged = TRUE;
3545         }
3546
3547     if (lpLVItem->mask & LVIF_TEXT)
3548         if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3549         {
3550             textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3551             *bChanged = TRUE;
3552         }
3553
3554     return TRUE;
3555 }
3556
3557 /***
3558  * DESCRIPTION:
3559  * Sets item attributes.
3560  *
3561  * PARAMETER(S):
3562  * [I] infoPtr : valid pointer to the listview structure
3563  * [I] lpLVItem : new item atttributes
3564  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3565  *
3566  * RETURN:
3567  *   SUCCESS : TRUE
3568  *   FAILURE : FALSE
3569  */
3570 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3571 {
3572     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3573     HWND hwndSelf = infoPtr->hwndSelf;
3574     LPWSTR pszText = NULL;
3575     BOOL bResult, bChanged = FALSE;
3576     
3577     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3578
3579     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3580         return FALSE;
3581
3582     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3583     if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3584     {
3585         pszText = lpLVItem->pszText;
3586         ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3587     }
3588     
3589     /* actually set the fields */
3590     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3591     
3592     if (lpLVItem->iSubItem)
3593         bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3594     else
3595         bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3596     if (!IsWindow(hwndSelf))
3597         return FALSE;
3598
3599     /* redraw item, if necessary */
3600     if (bChanged && !infoPtr->bIsDrawing)
3601     {
3602         /* this little optimization eliminates some nasty flicker */
3603         if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3604              (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3605             LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3606         else
3607             LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3608     }
3609     /* restore text */
3610     if (pszText)
3611     {
3612         textfreeT(lpLVItem->pszText, isW);
3613         ((LVITEMW *)lpLVItem)->pszText = pszText;
3614     }
3615
3616     return bResult;
3617 }
3618
3619 /***
3620  * DESCRIPTION:
3621  * Retrieves the index of the item at coordinate (0, 0) of the client area.
3622  *
3623  * PARAMETER(S):
3624  * [I] infoPtr : valid pointer to the listview structure
3625  *
3626  * RETURN:
3627  * item index
3628  */
3629 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3630 {
3631     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3632     INT nItem = 0;
3633     SCROLLINFO scrollInfo;
3634
3635     scrollInfo.cbSize = sizeof(SCROLLINFO);
3636     scrollInfo.fMask = SIF_POS;
3637
3638     if (uView == LVS_LIST)
3639     {
3640         if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3641             nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3642     }
3643     else if (uView == LVS_REPORT)
3644     {
3645         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3646             nItem = scrollInfo.nPos;
3647     } 
3648     else
3649     {
3650         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3651             nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3652     }
3653
3654     TRACE("nItem=%d\n", nItem);
3655     
3656     return nItem;
3657 }
3658
3659
3660 /***
3661  * DESCRIPTION:
3662  * Erases the background of the given rectangle
3663  *
3664  * PARAMETER(S):
3665  * [I] infoPtr : valid pointer to the listview structure
3666  * [I] hdc : device context handle
3667  * [I] lprcBox : clipping rectangle
3668  *
3669  * RETURN:
3670  *   Success: TRUE
3671  *   Failure: FALSE
3672  */
3673 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3674 {
3675     if (!infoPtr->hBkBrush) return FALSE;
3676
3677     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3678
3679     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3680 }
3681
3682 /***
3683  * DESCRIPTION:
3684  * Draws an item.
3685  *
3686  * PARAMETER(S):
3687  * [I] infoPtr : valid pointer to the listview structure
3688  * [I] hdc : device context handle
3689  * [I] nItem : item index
3690  * [I] nSubItem : subitem index
3691  * [I] pos : item position in client coordinates
3692  * [I] cdmode : custom draw mode
3693  *
3694  * RETURN:
3695  *   Success: TRUE
3696  *   Failure: FALSE
3697  */
3698 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3699 {
3700     UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3701     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3702     static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3703     DWORD cdsubitemmode = CDRF_DODEFAULT;
3704     RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3705     NMLVCUSTOMDRAW nmlvcd;
3706     HIMAGELIST himl;
3707     LVITEMW lvItem;
3708     HFONT hOldFont;
3709
3710     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3711
3712     /* get information needed for drawing the item */
3713     lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3714     if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3715     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3716     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3717     lvItem.iItem = nItem;
3718     lvItem.iSubItem = nSubItem;
3719     lvItem.state = 0;
3720     lvItem.lParam = 0;
3721     lvItem.cchTextMax = DISP_TEXT_SIZE;
3722     lvItem.pszText = szDispText;
3723     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3724     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
3725         lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3726     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3727     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3728
3729     /* now check if we need to update the focus rectangle */
3730     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3731
3732     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3733     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3734     OffsetRect(&rcBox, pos.x, pos.y);
3735     OffsetRect(&rcState, pos.x, pos.y);
3736     OffsetRect(&rcIcon, pos.x, pos.y);
3737     OffsetRect(&rcLabel, pos.x, pos.y);
3738     TRACE("    rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n", 
3739         wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcState),
3740         wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3741
3742     /* fill in the custom draw structure */
3743     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3744
3745     hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3746     if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3747     if (cdmode & CDRF_NOTIFYITEMDRAW)
3748         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3749     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3750     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3751     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3752     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3753     {
3754         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3755         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3756     }
3757     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3758         prepaint_setup(infoPtr, hdc, &nmlvcd);
3759
3760     /* in full row select, subitems, will just use main item's colors */
3761     if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3762         nmlvcd.clrTextBk = CLR_NONE;
3763     
3764     /* state icons */
3765     if (infoPtr->himlState && !IsRectEmpty(&rcState))
3766     {
3767         UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3768         if (uStateImage)
3769         {
3770              TRACE("uStateImage=%d\n", uStateImage);
3771              ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3772         }
3773     }
3774
3775     /* small icons */
3776     himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3777     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3778     {
3779         TRACE("iImage=%d\n", lvItem.iImage);
3780         ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3781                          rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3782                          (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3783     }
3784
3785     /* Don't bother painting item being edited */
3786     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3787
3788     /* draw the selection background, if we're drawing the main item */
3789     if (nSubItem == 0)
3790     {
3791         rcSelect = rcLabel;
3792         if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3793             rcSelect.right = rcBox.right;
3794    
3795         if (nmlvcd.clrTextBk != CLR_NONE) 
3796             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3797         if(lprcFocus) *lprcFocus = rcSelect;
3798     }
3799    
3800     /* figure out the text drawing flags */
3801     uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3802     if (uView == LVS_ICON)
3803         uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3804     else if (nSubItem)
3805     {
3806         switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3807         {
3808         case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
3809         case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3810         default:            uFormat |= DT_LEFT;
3811         }
3812     }
3813     if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3814     {
3815         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3816         else rcLabel.left += LABEL_HOR_PADDING;
3817     }
3818     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3819     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3820
3821 postpaint:
3822     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3823         notify_postpaint(infoPtr, &nmlvcd);
3824     if (cdsubitemmode & CDRF_NEWFONT)
3825         SelectObject(hdc, hOldFont);
3826     return TRUE;
3827 }
3828
3829 /***
3830  * DESCRIPTION:
3831  * Draws listview items when in owner draw mode.
3832  *
3833  * PARAMETER(S):
3834  * [I] infoPtr : valid pointer to the listview structure
3835  * [I] hdc : device context handle
3836  *
3837  * RETURN:
3838  * None
3839  */
3840 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3841 {
3842     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3843     DWORD cditemmode = CDRF_DODEFAULT;
3844     NMLVCUSTOMDRAW nmlvcd;
3845     POINT Origin, Position;
3846     DRAWITEMSTRUCT dis;
3847     LVITEMW item;
3848     
3849     TRACE("()\n");
3850
3851     ZeroMemory(&dis, sizeof(dis));
3852     
3853     /* Get scroll info once before loop */
3854     LISTVIEW_GetOrigin(infoPtr, &Origin);
3855     
3856     /* iterate through the invalidated rows */
3857     while(iterator_next(i))
3858     {
3859         item.iItem = i->nItem;
3860         item.iSubItem = 0;
3861         item.mask = LVIF_PARAM | LVIF_STATE;
3862         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3863         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3864            
3865         dis.CtlType = ODT_LISTVIEW;
3866         dis.CtlID = uID;
3867         dis.itemID = item.iItem;
3868         dis.itemAction = ODA_DRAWENTIRE;
3869         dis.itemState = 0;
3870         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3871         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3872         dis.hwndItem = infoPtr->hwndSelf;
3873         dis.hDC = hdc;
3874         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3875         dis.rcItem.left = Position.x + Origin.x;
3876         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3877         dis.rcItem.top = Position.y + Origin.y;
3878         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3879         dis.itemData = item.lParam;
3880
3881         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3882
3883     /*
3884      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3885      * structure for the rest. of the paint cycle
3886      */
3887         customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3888         if (cdmode & CDRF_NOTIFYITEMDRAW)
3889             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3890     
3891         if (!(cditemmode & CDRF_SKIPDEFAULT))
3892         {
3893             prepaint_setup (infoPtr, hdc, &nmlvcd);
3894             SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3895         }
3896
3897         if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3898             notify_postpaint(infoPtr, &nmlvcd);
3899     }
3900 }
3901
3902 /***
3903  * DESCRIPTION:
3904  * Draws listview items when in report display mode.
3905  *
3906  * PARAMETER(S):
3907  * [I] infoPtr : valid pointer to the listview structure
3908  * [I] hdc : device context handle
3909  * [I] cdmode : custom draw mode
3910  *
3911  * RETURN:
3912  * None
3913  */
3914 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3915 {
3916     INT rgntype;
3917     RECT rcClip, rcItem;
3918     POINT Origin, Position;
3919     RANGE colRange;
3920     ITERATOR j;
3921
3922     TRACE("()\n");
3923
3924     /* figure out what to draw */
3925     rgntype = GetClipBox(hdc, &rcClip);
3926     if (rgntype == NULLREGION) return;
3927     
3928     /* Get scroll info once before loop */
3929     LISTVIEW_GetOrigin(infoPtr, &Origin);
3930     
3931     /* narrow down the columns we need to paint */
3932     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3933     {
3934         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3935         if (rcItem.right + Origin.x >= rcClip.left) break;
3936     }
3937     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3938     {
3939         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3940         if (rcItem.left + Origin.x < rcClip.right) break;
3941     }
3942     iterator_rangeitems(&j, colRange);
3943
3944     /* in full row select, we _have_ to draw the main item */
3945     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3946         j.nSpecial = 0;
3947
3948     /* iterate through the invalidated rows */
3949     while(iterator_next(i))
3950     {
3951         /* iterate through the invalidated columns */
3952         while(iterator_next(&j))
3953         {
3954             LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3955             Position.x += Origin.x;
3956             Position.y += Origin.y;
3957
3958             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3959             {
3960                 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3961                 rcItem.top = 0;
3962                 rcItem.bottom = infoPtr->nItemHeight;
3963                 OffsetRect(&rcItem, Position.x, Position.y);
3964                 if (!RectVisible(hdc, &rcItem)) continue;
3965             }
3966
3967             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3968         }
3969     }
3970     iterator_destroy(&j);
3971 }
3972
3973 /***
3974  * DESCRIPTION:
3975  * Draws listview items when in list display mode.
3976  *
3977  * PARAMETER(S):
3978  * [I] infoPtr : valid pointer to the listview structure
3979  * [I] hdc : device context handle
3980  * [I] cdmode : custom draw mode
3981  *
3982  * RETURN:
3983  * None
3984  */
3985 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3986 {
3987     POINT Origin, Position;
3988
3989     /* Get scroll info once before loop */
3990     LISTVIEW_GetOrigin(infoPtr, &Origin);
3991     
3992     while(iterator_prev(i))
3993     {
3994         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3995         Position.x += Origin.x;
3996         Position.y += Origin.y;
3997
3998         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3999     }
4000 }
4001
4002
4003 /***
4004  * DESCRIPTION:
4005  * Draws listview items.
4006  *
4007  * PARAMETER(S):
4008  * [I] infoPtr : valid pointer to the listview structure
4009  * [I] hdc : device context handle
4010  *
4011  * RETURN:
4012  * NoneX
4013  */
4014 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
4015 {
4016     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4017     COLORREF oldTextColor, oldClrTextBk, oldClrText;
4018     NMLVCUSTOMDRAW nmlvcd;
4019     HFONT hOldFont;
4020     DWORD cdmode;
4021     INT oldBkMode;
4022     RECT rcClient;
4023     ITERATOR i;
4024
4025     LISTVIEW_DUMP(infoPtr);
4026   
4027     infoPtr->bIsDrawing = TRUE;
4028
4029     /* save dc values we're gonna trash while drawing */
4030     hOldFont = SelectObject(hdc, infoPtr->hFont);
4031     oldBkMode = GetBkMode(hdc);
4032     infoPtr->clrTextBkDefault = GetBkColor(hdc);
4033     oldTextColor = GetTextColor(hdc);
4034
4035     oldClrTextBk = infoPtr->clrTextBk;
4036     oldClrText   = infoPtr->clrText;
4037    
4038     infoPtr->cditemmode = CDRF_DODEFAULT;
4039
4040     GetClientRect(infoPtr->hwndSelf, &rcClient);
4041     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4042     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4043     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4044     prepaint_setup(infoPtr, hdc, &nmlvcd);
4045
4046     /* Use these colors to draw the items */
4047     infoPtr->clrTextBk = nmlvcd.clrTextBk;
4048     infoPtr->clrText = nmlvcd.clrText;
4049
4050     /* nothing to draw */
4051     if(infoPtr->nItemCount == 0) goto enddraw;
4052
4053     /* figure out what we need to draw */
4054     iterator_visibleitems(&i, infoPtr, hdc);
4055     
4056     /* send cache hint notification */
4057     if (infoPtr->dwStyle & LVS_OWNERDATA)
4058     {
4059         RANGE range = iterator_range(&i);
4060         NMLVCACHEHINT nmlv;
4061         
4062         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4063         nmlv.iFrom = range.lower;
4064         nmlv.iTo   = range.upper - 1;
4065         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4066     }
4067
4068     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4069         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4070     else
4071     {
4072         if (uView == LVS_REPORT)
4073             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4074         else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4075             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4076
4077         /* if we have a focus rect, draw it */
4078         if (infoPtr->bFocus)
4079             DrawFocusRect(hdc, &infoPtr->rcFocus);
4080     }
4081     iterator_destroy(&i);
4082     
4083 enddraw:
4084     if (cdmode & CDRF_NOTIFYPOSTPAINT)
4085         notify_postpaint(infoPtr, &nmlvcd);
4086
4087     infoPtr->clrTextBk = oldClrTextBk;
4088     infoPtr->clrText = oldClrText;
4089
4090     SelectObject(hdc, hOldFont);
4091     SetBkMode(hdc, oldBkMode);
4092     SetBkColor(hdc, infoPtr->clrTextBkDefault);
4093     SetTextColor(hdc, oldTextColor);
4094     infoPtr->bIsDrawing = FALSE;
4095 }
4096
4097
4098 /***
4099  * DESCRIPTION:
4100  * Calculates the approximate width and height of a given number of items.
4101  *
4102  * PARAMETER(S):
4103  * [I] infoPtr : valid pointer to the listview structure
4104  * [I] nItemCount : number of items
4105  * [I] wWidth : width
4106  * [I] wHeight : height
4107  *
4108  * RETURN:
4109  * Returns a DWORD. The width in the low word and the height in high word.
4110  */
4111 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4112                                             WORD wWidth, WORD wHeight)
4113 {
4114   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4115   INT nItemCountPerColumn = 1;
4116   INT nColumnCount = 0;
4117   DWORD dwViewRect = 0;
4118
4119   if (nItemCount == -1)
4120     nItemCount = infoPtr->nItemCount;
4121
4122   if (uView == LVS_LIST)
4123   {
4124     if (wHeight == 0xFFFF)
4125     {
4126       /* use current height */
4127       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4128     }
4129
4130     if (wHeight < infoPtr->nItemHeight)
4131       wHeight = infoPtr->nItemHeight;
4132
4133     if (nItemCount > 0)
4134     {
4135       if (infoPtr->nItemHeight > 0)
4136       {
4137         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4138         if (nItemCountPerColumn == 0)
4139           nItemCountPerColumn = 1;
4140
4141         if (nItemCount % nItemCountPerColumn != 0)
4142           nColumnCount = nItemCount / nItemCountPerColumn;
4143         else
4144           nColumnCount = nItemCount / nItemCountPerColumn + 1;
4145       }
4146     }
4147
4148     /* Microsoft padding magic */
4149     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4150     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4151
4152     dwViewRect = MAKELONG(wWidth, wHeight);
4153   }
4154   else if (uView == LVS_REPORT)
4155   {
4156     RECT rcBox;
4157
4158     if (infoPtr->nItemCount > 0)
4159     {
4160       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4161       wWidth = rcBox.right - rcBox.left;
4162       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4163     }
4164     else
4165     {
4166       /* use current height and width */
4167       if (wHeight == 0xffff)
4168           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4169       if (wWidth == 0xffff)
4170           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4171     }
4172
4173     dwViewRect = MAKELONG(wWidth, wHeight);
4174   }
4175   else if (uView == LVS_SMALLICON)
4176     FIXME("uView == LVS_SMALLICON: not implemented\n");
4177   else if (uView == LVS_ICON)
4178     FIXME("uView == LVS_ICON: not implemented\n");
4179
4180   return dwViewRect;
4181 }
4182
4183
4184 /***
4185  * DESCRIPTION:
4186  * Create a drag image list for the specified item.
4187  *
4188  * PARAMETER(S):
4189  * [I] infoPtr : valid pointer to the listview structure
4190  * [I] iItem   : index of item
4191  * [O] lppt    : Upperr-left corner of the image
4192  *
4193  * RETURN:
4194  * Returns a handle to the image list if successful, NULL otherwise.
4195  */
4196 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4197 {
4198     RECT rcItem;
4199     SIZE size;
4200     POINT pos;
4201     HDC hdc, hdcOrig;
4202     HBITMAP hbmp, hOldbmp;
4203     HIMAGELIST dragList = 0;
4204     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4205
4206     if (iItem < 0 || iItem >= infoPtr->nItemCount)
4207         return 0;
4208
4209     rcItem.left = LVIR_BOUNDS;
4210     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4211         return 0;
4212
4213     lppt->x = rcItem.left;
4214     lppt->y = rcItem.top;
4215
4216     size.cx = rcItem.right - rcItem.left;
4217     size.cy = rcItem.bottom - rcItem.top;
4218
4219     hdcOrig = GetDC(infoPtr->hwndSelf);
4220     hdc = CreateCompatibleDC(hdcOrig);
4221     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4222     hOldbmp = SelectObject(hdc, hbmp);
4223
4224     rcItem.left = rcItem.top = 0;
4225     rcItem.right = size.cx;
4226     rcItem.bottom = size.cy;
4227     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4228     
4229     pos.x = pos.y = 0;
4230     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4231     {
4232         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4233         SelectObject(hdc, hOldbmp);
4234         ImageList_Add(dragList, hbmp, 0);
4235     }
4236     else
4237         SelectObject(hdc, hOldbmp);
4238
4239     DeleteObject(hbmp);
4240     DeleteDC(hdc);
4241     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4242
4243     TRACE("ret=%p\n", dragList);
4244
4245     return dragList;
4246 }
4247
4248
4249 /***
4250  * DESCRIPTION:
4251  * Removes all listview items and subitems.
4252  *
4253  * PARAMETER(S):
4254  * [I] infoPtr : valid pointer to the listview structure
4255  *
4256  * RETURN:
4257  *   SUCCESS : TRUE
4258  *   FAILURE : FALSE
4259  */
4260 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4261 {
4262     NMLISTVIEW nmlv;
4263     HDPA hdpaSubItems = NULL;
4264     BOOL bSuppress;
4265     ITEMHDR *hdrItem;
4266     INT i, j;
4267
4268     TRACE("()\n");
4269
4270     /* we do it directly, to avoid notifications */
4271     ranges_clear(infoPtr->selectionRanges);
4272     infoPtr->nSelectionMark = -1;
4273     infoPtr->nFocusedItem = -1;
4274     SetRectEmpty(&infoPtr->rcFocus);
4275     /* But we are supposed to leave nHotItem as is! */
4276
4277
4278     /* send LVN_DELETEALLITEMS notification */
4279     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4280     nmlv.iItem = -1;
4281     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4282
4283     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4284     {
4285         /* send LVN_DELETEITEM notification, if not suppressed */
4286         if (!bSuppress) notify_deleteitem(infoPtr, i);
4287         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4288         {
4289             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4290             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4291             {
4292                 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4293                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4294                 Free(hdrItem);
4295             }
4296             DPA_Destroy(hdpaSubItems);
4297             DPA_DeletePtr(infoPtr->hdpaItems, i);
4298         }
4299         DPA_DeletePtr(infoPtr->hdpaPosX, i);
4300         DPA_DeletePtr(infoPtr->hdpaPosY, i);
4301         infoPtr->nItemCount --;
4302     }
4303     
4304     LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4305     LISTVIEW_UpdateScroll(infoPtr);
4306     LISTVIEW_InvalidateList(infoPtr);
4307     
4308     return TRUE;
4309 }
4310
4311 /***
4312  * DESCRIPTION:
4313  * Scrolls, and updates the columns, when a column is changing width.
4314  *
4315  * PARAMETER(S):
4316  * [I] infoPtr : valid pointer to the listview structure
4317  * [I] nColumn : column to scroll
4318  * [I] dx : amount of scroll, in pixels
4319  *
4320  * RETURN:
4321  *   None.
4322  */
4323 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4324 {
4325     COLUMN_INFO *lpColumnInfo;
4326     RECT rcOld, rcCol;
4327     POINT ptOrigin;
4328     INT nCol;
4329    
4330     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4331     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4332     rcCol = lpColumnInfo->rcHeader;
4333     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4334         rcCol.left = rcCol.right;
4335     
4336     /* ajust the other columns */
4337     for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4338     {
4339         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4340         lpColumnInfo->rcHeader.left += dx;
4341         lpColumnInfo->rcHeader.right += dx;
4342     }
4343
4344     /* do not update screen if not in report mode */
4345     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4346     
4347     /* if we have a focus, must first erase the focus rect */
4348     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4349     
4350     /* Need to reset the item width when inserting a new column */
4351     infoPtr->nItemWidth += dx;
4352
4353     LISTVIEW_UpdateScroll(infoPtr);
4354     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4355
4356     /* scroll to cover the deleted column, and invalidate for redraw */
4357     rcOld = infoPtr->rcList;
4358     rcOld.left = ptOrigin.x + rcCol.left + dx;
4359     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4360     
4361     /* we can restore focus now */
4362     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4363 }
4364
4365 /***
4366  * DESCRIPTION:
4367  * Removes a column from the listview control.
4368  *
4369  * PARAMETER(S):
4370  * [I] infoPtr : valid pointer to the listview structure
4371  * [I] nColumn : column index
4372  *
4373  * RETURN:
4374  *   SUCCESS : TRUE
4375  *   FAILURE : FALSE
4376  */
4377 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4378 {
4379     RECT rcCol;
4380     
4381     TRACE("nColumn=%d\n", nColumn);
4382
4383     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4384            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4385
4386     /* While the MSDN specifically says that column zero should not be deleted,
4387        what actually happens is that the column itself is deleted but no items or subitems
4388        are removed.
4389      */
4390
4391     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4392     
4393     if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4394         return FALSE;
4395
4396     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4397     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4398   
4399     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4400     {
4401         SUBITEM_INFO *lpSubItem, *lpDelItem;
4402         HDPA hdpaSubItems;
4403         INT nItem, nSubItem, i;
4404         
4405         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4406         {
4407             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4408             nSubItem = 0;
4409             lpDelItem = 0;
4410             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4411             {
4412                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4413                 if (lpSubItem->iSubItem == nColumn)
4414                 {
4415                     nSubItem = i;
4416                     lpDelItem = lpSubItem;
4417                 }
4418                 else if (lpSubItem->iSubItem > nColumn) 
4419                 {
4420                     lpSubItem->iSubItem--;
4421                 }
4422             }
4423
4424             /* if we found our subitem, zapp it */      
4425             if (nSubItem > 0)
4426             {
4427                 /* free string */
4428                 if (is_textW(lpDelItem->hdr.pszText))
4429                     Free(lpDelItem->hdr.pszText);
4430
4431                 /* free item */
4432                 Free(lpDelItem);
4433
4434                 /* free dpa memory */
4435                 DPA_DeletePtr(hdpaSubItems, nSubItem);
4436             }
4437         }
4438     }
4439
4440     /* update the other column info */
4441     LISTVIEW_UpdateItemSize(infoPtr);
4442     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4443         LISTVIEW_InvalidateList(infoPtr);
4444     else
4445         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4446
4447     return TRUE;
4448 }
4449
4450 /***
4451  * DESCRIPTION:
4452  * Invalidates the listview after an item's insertion or deletion.
4453  *
4454  * PARAMETER(S):
4455  * [I] infoPtr : valid pointer to the listview structure
4456  * [I] nItem : item index
4457  * [I] dir : -1 if deleting, 1 if inserting
4458  *
4459  * RETURN:
4460  *   None
4461  */
4462 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4463 {
4464     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4465     INT nPerCol, nItemCol, nItemRow;
4466     RECT rcScroll;
4467     POINT Origin;
4468
4469     /* if we don't refresh, what's the point of scrolling? */
4470     if (!is_redrawing(infoPtr)) return;
4471     
4472     assert (abs(dir) == 1);
4473
4474     /* arrange icons if autoarrange is on */
4475     if (is_autoarrange(infoPtr))
4476     {
4477         BOOL arrange = TRUE;
4478         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4479         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4480         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4481     }
4482
4483     /* scrollbars need updating */
4484     LISTVIEW_UpdateScroll(infoPtr);
4485
4486     /* figure out the item's position */ 
4487     if (uView == LVS_REPORT)
4488         nPerCol = infoPtr->nItemCount + 1;
4489     else if (uView == LVS_LIST)
4490         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4491     else /* LVS_ICON, or LVS_SMALLICON */
4492         return;
4493     
4494     nItemCol = nItem / nPerCol;
4495     nItemRow = nItem % nPerCol;
4496     LISTVIEW_GetOrigin(infoPtr, &Origin);
4497
4498     /* move the items below up a slot */
4499     rcScroll.left = nItemCol * infoPtr->nItemWidth;
4500     rcScroll.top = nItemRow * infoPtr->nItemHeight;
4501     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4502     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4503     OffsetRect(&rcScroll, Origin.x, Origin.y);
4504     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4505     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4506     {
4507         TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4508         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
4509                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4510     }
4511
4512     /* report has only that column, so we're done */
4513     if (uView == LVS_REPORT) return;
4514
4515     /* now for LISTs, we have to deal with the columns to the right */
4516     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4517     rcScroll.top = 0;
4518     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4519     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4520     OffsetRect(&rcScroll, Origin.x, Origin.y);
4521     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4522         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4523                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4524 }
4525
4526 /***
4527  * DESCRIPTION:
4528  * Removes an item from the listview control.
4529  *
4530  * PARAMETER(S):
4531  * [I] infoPtr : valid pointer to the listview structure
4532  * [I] nItem : item index
4533  *
4534  * RETURN:
4535  *   SUCCESS : TRUE
4536  *   FAILURE : FALSE
4537  */
4538 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4539 {
4540     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4541     LVITEMW item;
4542
4543     TRACE("(nItem=%d)\n", nItem);
4544
4545     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4546     
4547     /* remove selection, and focus */
4548     item.state = 0;
4549     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4550     LISTVIEW_SetItemState(infoPtr, nItem, &item);
4551             
4552     /* send LVN_DELETEITEM notification. */
4553     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4554
4555     /* we need to do this here, because we'll be deleting stuff */  
4556     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4557         LISTVIEW_InvalidateItem(infoPtr, nItem);
4558     
4559     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4560     {
4561         HDPA hdpaSubItems;
4562         ITEMHDR *hdrItem;
4563         INT i;
4564
4565         hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);  
4566         for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4567         {
4568             hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4569             if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4570             Free(hdrItem);
4571         }
4572         DPA_Destroy(hdpaSubItems);
4573     }
4574
4575     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4576     {
4577         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4578         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4579     }
4580
4581     infoPtr->nItemCount--;
4582     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4583
4584     /* now is the invalidation fun */
4585     LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4586     return TRUE;
4587 }
4588
4589
4590 /***
4591  * DESCRIPTION:
4592  * Callback implementation for editlabel control
4593  *
4594  * PARAMETER(S):
4595  * [I] infoPtr : valid pointer to the listview structure
4596  * [I] pszText : modified text
4597  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4598  *
4599  * RETURN:
4600  *   SUCCESS : TRUE
4601  *   FAILURE : FALSE
4602  */
4603 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4604 {
4605     HWND hwndSelf = infoPtr->hwndSelf;
4606     NMLVDISPINFOW dispInfo;
4607
4608     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4609
4610     ZeroMemory(&dispInfo, sizeof(dispInfo));
4611     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4612     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4613     dispInfo.item.iSubItem = 0;
4614     dispInfo.item.stateMask = ~0;
4615     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4616     /* add the text from the edit in */
4617     dispInfo.item.mask |= LVIF_TEXT;
4618     dispInfo.item.pszText = pszText;
4619     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4620
4621     /* Do we need to update the Item Text */
4622     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4623     if (!IsWindow(hwndSelf))
4624         return FALSE;
4625     if (!pszText) return TRUE;
4626
4627     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4628     {
4629         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4630         ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4631         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4632         {
4633             LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4634             return TRUE;
4635         }
4636     }
4637
4638     ZeroMemory(&dispInfo, sizeof(dispInfo));
4639     dispInfo.item.mask = LVIF_TEXT;
4640     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4641     dispInfo.item.iSubItem = 0;
4642     dispInfo.item.pszText = pszText;
4643     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4644     return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4645 }
4646
4647 /***
4648  * DESCRIPTION:
4649  * Begin in place editing of specified list view item
4650  *
4651  * PARAMETER(S):
4652  * [I] infoPtr : valid pointer to the listview structure
4653  * [I] nItem : item index
4654  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4655  *
4656  * RETURN:
4657  *   SUCCESS : TRUE
4658  *   FAILURE : FALSE
4659  */
4660 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4661 {
4662     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4663     NMLVDISPINFOW dispInfo;
4664     RECT rect;
4665     HWND hwndSelf = infoPtr->hwndSelf;
4666
4667     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4668
4669     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4670     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4671
4672     infoPtr->nEditLabelItem = nItem;
4673
4674     /* Is the EditBox still there, if so remove it */
4675     if(infoPtr->hwndEdit != 0)
4676     {
4677         SetFocus(infoPtr->hwndSelf);
4678         infoPtr->hwndEdit = 0;
4679     }
4680
4681     LISTVIEW_SetSelection(infoPtr, nItem);
4682     LISTVIEW_SetItemFocus(infoPtr, nItem);
4683     LISTVIEW_InvalidateItem(infoPtr, nItem);
4684
4685     rect.left = LVIR_LABEL;
4686     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4687     
4688     ZeroMemory(&dispInfo, sizeof(dispInfo));
4689     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4690     dispInfo.item.iItem = nItem;
4691     dispInfo.item.iSubItem = 0;
4692     dispInfo.item.stateMask = ~0;
4693     dispInfo.item.pszText = szDispText;
4694     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4695     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4696
4697     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4698                     rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4699     if (!infoPtr->hwndEdit) return 0;
4700     
4701     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4702     {
4703         if (!IsWindow(hwndSelf))
4704             return 0;
4705         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4706         infoPtr->hwndEdit = 0;
4707         return 0;
4708     }
4709
4710     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4711     SetFocus(infoPtr->hwndEdit);
4712     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4713     return infoPtr->hwndEdit;
4714 }
4715
4716
4717 /***
4718  * DESCRIPTION:
4719  * Ensures the specified item is visible, scrolling into view if necessary.
4720  *
4721  * PARAMETER(S):
4722  * [I] infoPtr : valid pointer to the listview structure
4723  * [I] nItem : item index
4724  * [I] bPartial : partially or entirely visible
4725  *
4726  * RETURN:
4727  *   SUCCESS : TRUE
4728  *   FAILURE : FALSE
4729  */
4730 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4731 {
4732     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4733     INT nScrollPosHeight = 0;
4734     INT nScrollPosWidth = 0;
4735     INT nHorzAdjust = 0;
4736     INT nVertAdjust = 0;
4737     INT nHorzDiff = 0;
4738     INT nVertDiff = 0;
4739     RECT rcItem, rcTemp;
4740
4741     rcItem.left = LVIR_BOUNDS;
4742     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4743
4744     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4745     
4746     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4747     {
4748         /* scroll left/right, but in LVS_REPORT mode */
4749         if (uView == LVS_LIST)
4750             nScrollPosWidth = infoPtr->nItemWidth;
4751         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4752             nScrollPosWidth = 1;
4753
4754         if (rcItem.left < infoPtr->rcList.left)
4755         {
4756             nHorzAdjust = -1;
4757             if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4758         }
4759         else
4760         {
4761             nHorzAdjust = 1;
4762             if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4763         }
4764     }
4765
4766     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4767     {
4768         /* scroll up/down, but not in LVS_LIST mode */
4769         if (uView == LVS_REPORT)
4770             nScrollPosHeight = infoPtr->nItemHeight;
4771         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4772             nScrollPosHeight = 1;
4773
4774         if (rcItem.top < infoPtr->rcList.top)
4775         {
4776             nVertAdjust = -1;
4777             if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4778         }
4779         else
4780         {
4781             nVertAdjust = 1;
4782             if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4783         }
4784     }
4785
4786     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4787
4788     if (nScrollPosWidth)
4789     {
4790         INT diff = nHorzDiff / nScrollPosWidth;
4791         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4792         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4793     }
4794
4795     if (nScrollPosHeight)
4796     {
4797         INT diff = nVertDiff / nScrollPosHeight;
4798         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4799         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4800     }
4801
4802     return TRUE;
4803 }
4804
4805 /***
4806  * DESCRIPTION:
4807  * Searches for an item with specific characteristics.
4808  *
4809  * PARAMETER(S):
4810  * [I] hwnd : window handle
4811  * [I] nStart : base item index
4812  * [I] lpFindInfo : item information to look for
4813  *
4814  * RETURN:
4815  *   SUCCESS : index of item
4816  *   FAILURE : -1
4817  */
4818 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4819                               const LVFINDINFOW *lpFindInfo)
4820 {
4821     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4822     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4823     BOOL bWrap = FALSE, bNearest = FALSE;
4824     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4825     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4826     POINT Position, Destination;
4827     LVITEMW lvItem;
4828
4829     if (!lpFindInfo || nItem < 0) return -1;
4830     
4831     lvItem.mask = 0;
4832     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4833     {
4834         lvItem.mask |= LVIF_TEXT;
4835         lvItem.pszText = szDispText;
4836         lvItem.cchTextMax = DISP_TEXT_SIZE;
4837     }
4838
4839     if (lpFindInfo->flags & LVFI_WRAP)
4840         bWrap = TRUE;
4841
4842     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
4843         (uView == LVS_ICON || uView ==LVS_SMALLICON))
4844     {
4845         POINT Origin;
4846         RECT rcArea;
4847         
4848         LISTVIEW_GetOrigin(infoPtr, &Origin);
4849         Destination.x = lpFindInfo->pt.x - Origin.x;
4850         Destination.y = lpFindInfo->pt.y - Origin.y;
4851         switch(lpFindInfo->vkDirection)
4852         {
4853         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
4854         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
4855         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4856         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
4857         case VK_HOME:  Destination.x = Destination.y = 0; break;
4858         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4859         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4860         case VK_END:
4861             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4862             Destination.x = rcArea.right; 
4863             Destination.y = rcArea.bottom; 
4864             break;
4865         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4866         }
4867         bNearest = TRUE;
4868     }
4869     else Destination.x = Destination.y = 0;
4870
4871     /* if LVFI_PARAM is specified, all other flags are ignored */
4872     if (lpFindInfo->flags & LVFI_PARAM)
4873     {
4874         lvItem.mask |= LVIF_PARAM;
4875         bNearest = FALSE;
4876         lvItem.mask &= ~LVIF_TEXT;
4877     }
4878
4879 again:
4880     for (; nItem < nLast; nItem++)
4881     {
4882         lvItem.iItem = nItem;
4883         lvItem.iSubItem = 0;
4884         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4885
4886         if (lvItem.mask & LVIF_PARAM)
4887         {
4888             if (lpFindInfo->lParam == lvItem.lParam)
4889                 return nItem;
4890             else
4891                 continue;
4892         }
4893         
4894         if (lvItem.mask & LVIF_TEXT)
4895         {
4896             if (lpFindInfo->flags & LVFI_PARTIAL)
4897             {
4898                 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4899             }
4900             else
4901             {
4902                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4903             }
4904         }
4905
4906         if (!bNearest) return nItem;
4907         
4908         /* This is very inefficient. To do a good job here,
4909          * we need a sorted array of (x,y) item positions */
4910         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4911
4912         /* compute the distance^2 to the destination */
4913         xdist = Destination.x - Position.x;
4914         ydist = Destination.y - Position.y;
4915         dist = xdist * xdist + ydist * ydist;
4916
4917         /* remember the distance, and item if it's closer */
4918         if (dist < mindist)
4919         {
4920             mindist = dist;
4921             nNearestItem = nItem;
4922         }
4923     }
4924
4925     if (bWrap)
4926     {
4927         nItem = 0;
4928         nLast = min(nStart + 1, infoPtr->nItemCount);
4929         bWrap = FALSE;
4930         goto again;
4931     }
4932
4933     return nNearestItem;
4934 }
4935
4936 /***
4937  * DESCRIPTION:
4938  * Searches for an item with specific characteristics.
4939  *
4940  * PARAMETER(S):
4941  * [I] hwnd : window handle
4942  * [I] nStart : base item index
4943  * [I] lpFindInfo : item information to look for
4944  *
4945  * RETURN:
4946  *   SUCCESS : index of item
4947  *   FAILURE : -1
4948  */
4949 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4950                               const LVFINDINFOA *lpFindInfo)
4951 {
4952     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4953     LVFINDINFOW fiw;
4954     INT res;
4955
4956     memcpy(&fiw, lpFindInfo, sizeof(fiw));
4957     if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4958     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4959     if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4960     return res;
4961 }
4962
4963 /***
4964  * DESCRIPTION:
4965  * Retrieves the background image of the listview control.
4966  *
4967  * PARAMETER(S):
4968  * [I] infoPtr : valid pointer to the listview structure
4969  * [O] lpBkImage : background image attributes
4970  *
4971  * RETURN:
4972  *   SUCCESS : TRUE
4973  *   FAILURE : FALSE
4974  */
4975 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
4976 /* {   */
4977 /*   FIXME (listview, "empty stub!\n"); */
4978 /*   return FALSE;   */
4979 /* }   */
4980
4981 /***
4982  * DESCRIPTION:
4983  * Retrieves column attributes.
4984  *
4985  * PARAMETER(S):
4986  * [I] infoPtr : valid pointer to the listview structure
4987  * [I] nColumn :  column index
4988  * [IO] lpColumn : column information
4989  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4990  *           otherwise it is in fact a LPLVCOLUMNA
4991  *
4992  * RETURN:
4993  *   SUCCESS : TRUE
4994  *   FAILURE : FALSE
4995  */
4996 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4997 {
4998     COLUMN_INFO *lpColumnInfo;
4999     HDITEMW hdi;
5000
5001     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5002     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5003
5004     /* initialize memory */
5005     ZeroMemory(&hdi, sizeof(hdi));
5006
5007     if (lpColumn->mask & LVCF_TEXT)
5008     {
5009         hdi.mask |= HDI_TEXT;
5010         hdi.pszText = lpColumn->pszText;
5011         hdi.cchTextMax = lpColumn->cchTextMax;
5012     }
5013
5014     if (lpColumn->mask & LVCF_IMAGE)
5015         hdi.mask |= HDI_IMAGE;
5016
5017     if (lpColumn->mask & LVCF_ORDER)
5018         hdi.mask |= HDI_ORDER;
5019
5020     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5021
5022     if (lpColumn->mask & LVCF_FMT)
5023         lpColumn->fmt = lpColumnInfo->fmt;
5024
5025     if (lpColumn->mask & LVCF_WIDTH)
5026         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5027
5028     if (lpColumn->mask & LVCF_IMAGE)
5029         lpColumn->iImage = hdi.iImage;
5030
5031     if (lpColumn->mask & LVCF_ORDER)
5032         lpColumn->iOrder = hdi.iOrder;
5033
5034     return TRUE;
5035 }
5036
5037
5038 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5039 {
5040     INT i;
5041
5042     if (!lpiArray)
5043         return FALSE;
5044
5045     /* FIXME: little hack */
5046     for (i = 0; i < iCount; i++)
5047         lpiArray[i] = i;
5048
5049     return TRUE;
5050 }
5051
5052 /***
5053  * DESCRIPTION:
5054  * Retrieves the column width.
5055  *
5056  * PARAMETER(S):
5057  * [I] infoPtr : valid pointer to the listview structure
5058  * [I] int : column index
5059  *
5060  * RETURN:
5061  *   SUCCESS : column width
5062  *   FAILURE : zero
5063  */
5064 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5065 {
5066     INT nColumnWidth = 0;
5067     RECT rcHeader;
5068
5069     TRACE("nColumn=%d\n", nColumn);
5070
5071     /* we have a 'column' in LIST and REPORT mode only */
5072     switch(infoPtr->dwStyle & LVS_TYPEMASK)
5073     {
5074     case LVS_LIST:
5075         nColumnWidth = infoPtr->nItemWidth;
5076         break;
5077     case LVS_REPORT:
5078         if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5079         LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5080         nColumnWidth = rcHeader.right - rcHeader.left;
5081         break;
5082     }
5083
5084     TRACE("nColumnWidth=%d\n", nColumnWidth);
5085     return nColumnWidth;
5086 }
5087
5088 /***
5089  * DESCRIPTION:
5090  * In list or report display mode, retrieves the number of items that can fit
5091  * vertically in the visible area. In icon or small icon display mode,
5092  * retrieves the total number of visible items.
5093  *
5094  * PARAMETER(S):
5095  * [I] infoPtr : valid pointer to the listview structure
5096  *
5097  * RETURN:
5098  * Number of fully visible items.
5099  */
5100 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5101 {
5102     switch (infoPtr->dwStyle & LVS_TYPEMASK)
5103     {
5104     case LVS_ICON:
5105     case LVS_SMALLICON:
5106         return infoPtr->nItemCount;
5107     case LVS_REPORT:
5108         return LISTVIEW_GetCountPerColumn(infoPtr);
5109     case LVS_LIST:
5110         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5111     }
5112     assert(FALSE);
5113     return 0;
5114 }
5115
5116 /***
5117  * DESCRIPTION:
5118  * Retrieves an image list handle.
5119  *
5120  * PARAMETER(S):
5121  * [I] infoPtr : valid pointer to the listview structure
5122  * [I] nImageList : image list identifier
5123  *
5124  * RETURN:
5125  *   SUCCESS : image list handle
5126  *   FAILURE : NULL
5127  */
5128 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5129 {
5130     switch (nImageList)
5131     {
5132     case LVSIL_NORMAL: return infoPtr->himlNormal;
5133     case LVSIL_SMALL: return infoPtr->himlSmall;
5134     case LVSIL_STATE: return infoPtr->himlState;
5135     }
5136     return NULL;
5137 }
5138
5139 /* LISTVIEW_GetISearchString */
5140
5141 /***
5142  * DESCRIPTION:
5143  * Retrieves item attributes.
5144  *
5145  * PARAMETER(S):
5146  * [I] hwnd : window handle
5147  * [IO] lpLVItem : item info
5148  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5149  *           if FALSE, then lpLVItem is a LPLVITEMA.
5150  *
5151  * NOTE:
5152  *   This is the internal 'GetItem' interface -- it tries to
5153  *   be smart and avoid text copies, if possible, by modifying
5154  *   lpLVItem->pszText to point to the text string. Please note
5155  *   that this is not always possible (e.g. OWNERDATA), so on
5156  *   entry you *must* supply valid values for pszText, and cchTextMax.
5157  *   The only difference to the documented interface is that upon
5158  *   return, you should use *only* the lpLVItem->pszText, rather than
5159  *   the buffer pointer you provided on input. Most code already does
5160  *   that, so it's not a problem.
5161  *   For the two cases when the text must be copied (that is,
5162  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5163  *
5164  * RETURN:
5165  *   SUCCESS : TRUE
5166  *   FAILURE : FALSE
5167  */
5168 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5169 {
5170     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5171     NMLVDISPINFOW dispInfo;
5172     ITEM_INFO *lpItem;
5173     ITEMHDR* pItemHdr;
5174     HDPA hdpaSubItems;
5175     INT isubitem;
5176
5177     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5178
5179     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5180         return FALSE;
5181
5182     if (lpLVItem->mask == 0) return TRUE;
5183
5184     /* make a local copy */
5185     isubitem = lpLVItem->iSubItem;
5186
5187     /* a quick optimization if all we're asked is the focus state
5188      * these queries are worth optimising since they are common,
5189      * and can be answered in constant time, without the heavy accesses */
5190     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5191          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5192     {
5193         lpLVItem->state = 0;
5194         if (infoPtr->nFocusedItem == lpLVItem->iItem)
5195             lpLVItem->state |= LVIS_FOCUSED;
5196         return TRUE;
5197     }
5198
5199     ZeroMemory(&dispInfo, sizeof(dispInfo));
5200
5201     /* if the app stores all the data, handle it separately */
5202     if (infoPtr->dwStyle & LVS_OWNERDATA)
5203     {
5204         dispInfo.item.state = 0;
5205
5206         /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5207         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5208         {
5209             /* NOTE: copy only fields which we _know_ are initialized, some apps
5210              *       depend on the uninitialized fields being 0 */
5211             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5212             dispInfo.item.iItem = lpLVItem->iItem;
5213             dispInfo.item.iSubItem = isubitem;
5214             if (lpLVItem->mask & LVIF_TEXT)
5215             {
5216                 dispInfo.item.pszText = lpLVItem->pszText;
5217                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;                
5218             }
5219             if (lpLVItem->mask & LVIF_STATE)
5220                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5221             notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5222             dispInfo.item.stateMask = lpLVItem->stateMask;
5223             if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5224             {
5225                 /* full size structure expected - _WIN32IE >= 0x560 */
5226                 *lpLVItem = dispInfo.item;
5227             }
5228             else if (lpLVItem->mask & LVIF_INDENT)
5229             {
5230                 /* indent member expected - _WIN32IE >= 0x300 */
5231                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5232             }
5233             else
5234             {
5235                 /* minimal structure expected */
5236                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5237             }
5238             TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5239         }
5240         
5241         /* make sure lParam is zeroed out */
5242         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5243         
5244         /* we store only a little state, so if we're not asked, we're done */
5245         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5246
5247         /* if focus is handled by us, report it */
5248         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5249         {
5250             lpLVItem->state &= ~LVIS_FOCUSED;
5251             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5252                 lpLVItem->state |= LVIS_FOCUSED;
5253         }
5254
5255         /* and do the same for selection, if we handle it */
5256         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5257         {
5258             lpLVItem->state &= ~LVIS_SELECTED;
5259             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5260                 lpLVItem->state |= LVIS_SELECTED;
5261         }
5262         
5263         return TRUE;
5264     }
5265
5266     /* find the item and subitem structures before we proceed */
5267     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5268     lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5269     assert (lpItem);
5270
5271     if (isubitem)
5272     {
5273         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5274         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5275         if (!lpSubItem)
5276         {
5277             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5278             isubitem = 0;
5279         }
5280     }
5281     else
5282         pItemHdr = &lpItem->hdr;
5283
5284     /* Do we need to query the state from the app? */
5285     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5286     {
5287         dispInfo.item.mask |= LVIF_STATE;
5288         dispInfo.item.stateMask = infoPtr->uCallbackMask;
5289     }
5290   
5291     /* Do we need to enquire about the image? */
5292     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5293         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5294     {
5295         dispInfo.item.mask |= LVIF_IMAGE;
5296         dispInfo.item.iImage = I_IMAGECALLBACK;
5297     }
5298
5299     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5300     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5301     {
5302         dispInfo.item.mask |= LVIF_TEXT;
5303         dispInfo.item.pszText = lpLVItem->pszText;
5304         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5305         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5306             *dispInfo.item.pszText = '\0';
5307     }
5308
5309     /* If we don't have all the requested info, query the application */
5310     if (dispInfo.item.mask != 0)
5311     {
5312         dispInfo.item.iItem = lpLVItem->iItem;
5313         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5314         dispInfo.item.lParam = lpItem->lParam;
5315         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5316         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5317     }
5318
5319     /* we should not store values for subitems */
5320     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5321
5322     /* Now, handle the iImage field */
5323     if (dispInfo.item.mask & LVIF_IMAGE)
5324     {
5325         lpLVItem->iImage = dispInfo.item.iImage;
5326         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5327             pItemHdr->iImage = dispInfo.item.iImage;
5328     }
5329     else if (lpLVItem->mask & LVIF_IMAGE)
5330     {
5331         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5332             lpLVItem->iImage = pItemHdr->iImage;
5333         else
5334             lpLVItem->iImage = 0;
5335     }
5336
5337     /* The pszText field */
5338     if (dispInfo.item.mask & LVIF_TEXT)
5339     {
5340         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5341             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5342
5343         lpLVItem->pszText = dispInfo.item.pszText;
5344     }
5345     else if (lpLVItem->mask & LVIF_TEXT)
5346     {
5347         if (isW) lpLVItem->pszText = pItemHdr->pszText;
5348         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5349     }
5350
5351     /* if this is a subitem, we're done */
5352     if (isubitem) return TRUE;
5353   
5354     /* Next is the lParam field */
5355     if (dispInfo.item.mask & LVIF_PARAM)
5356     {
5357         lpLVItem->lParam = dispInfo.item.lParam;
5358         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5359             lpItem->lParam = dispInfo.item.lParam;
5360     }
5361     else if (lpLVItem->mask & LVIF_PARAM)
5362         lpLVItem->lParam = lpItem->lParam;
5363
5364     /* ... the state field (this one is different due to uCallbackmask) */
5365     if (lpLVItem->mask & LVIF_STATE) 
5366     {
5367         lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5368         if (dispInfo.item.mask & LVIF_STATE)
5369         {
5370             lpLVItem->state &= ~dispInfo.item.stateMask;
5371             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5372         }
5373         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5374         {
5375             lpLVItem->state &= ~LVIS_FOCUSED;
5376             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5377                 lpLVItem->state |= LVIS_FOCUSED;
5378         }
5379         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5380         {
5381             lpLVItem->state &= ~LVIS_SELECTED;
5382             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5383                 lpLVItem->state |= LVIS_SELECTED;
5384         }           
5385     }
5386
5387     /* and last, but not least, the indent field */
5388     if (lpLVItem->mask & LVIF_INDENT)
5389         lpLVItem->iIndent = lpItem->iIndent;
5390
5391     return TRUE;
5392 }
5393
5394 /***
5395  * DESCRIPTION:
5396  * Retrieves item attributes.
5397  *
5398  * PARAMETER(S):
5399  * [I] hwnd : window handle
5400  * [IO] lpLVItem : item info
5401  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5402  *           if FALSE, then lpLVItem is a LPLVITEMA.
5403  *
5404  * NOTE:
5405  *   This is the external 'GetItem' interface -- it properly copies
5406  *   the text in the provided buffer.
5407  *
5408  * RETURN:
5409  *   SUCCESS : TRUE
5410  *   FAILURE : FALSE
5411  */
5412 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5413 {
5414     LPWSTR pszText;
5415     BOOL bResult;
5416
5417     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5418         return FALSE;
5419
5420     pszText = lpLVItem->pszText;
5421     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5422     if (bResult && lpLVItem->pszText != pszText)
5423         textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5424     lpLVItem->pszText = pszText;
5425
5426     return bResult;
5427 }
5428
5429
5430 /***
5431  * DESCRIPTION:
5432  * Retrieves the position (upper-left) of the listview control item.
5433  * Note that for LVS_ICON style, the upper-left is that of the icon
5434  * and not the bounding box.
5435  *
5436  * PARAMETER(S):
5437  * [I] infoPtr : valid pointer to the listview structure
5438  * [I] nItem : item index
5439  * [O] lpptPosition : coordinate information
5440  *
5441  * RETURN:
5442  *   SUCCESS : TRUE
5443  *   FAILURE : FALSE
5444  */
5445 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5446 {
5447     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5448     POINT Origin;
5449
5450     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5451
5452     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5453
5454     LISTVIEW_GetOrigin(infoPtr, &Origin);
5455     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5456
5457     if (uView == LVS_ICON)
5458     {
5459         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5460         lpptPosition->y += ICON_TOP_PADDING;
5461     }
5462     lpptPosition->x += Origin.x;
5463     lpptPosition->y += Origin.y;
5464     
5465     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5466     return TRUE;
5467 }
5468
5469
5470 /***
5471  * DESCRIPTION:
5472  * Retrieves the bounding rectangle for a listview control item.
5473  *
5474  * PARAMETER(S):
5475  * [I] infoPtr : valid pointer to the listview structure
5476  * [I] nItem : item index
5477  * [IO] lprc : bounding rectangle coordinates
5478  *     lprc->left specifies the portion of the item for which the bounding
5479  *     rectangle will be retrieved.
5480  *
5481  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5482  *        including the icon and label.
5483  *         *
5484  *         * For LVS_ICON
5485  *         * Experiment shows that native control returns:
5486  *         *  width = min (48, length of text line)
5487  *         *    .left = position.x - (width - iconsize.cx)/2
5488  *         *    .right = .left + width
5489  *         *  height = #lines of text * ntmHeight + icon height + 8
5490  *         *    .top = position.y - 2
5491  *         *    .bottom = .top + height
5492  *         *  separation between items .y = itemSpacing.cy - height
5493  *         *                           .x = itemSpacing.cx - width
5494  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5495  *         *
5496  *         * For LVS_ICON
5497  *         * Experiment shows that native control returns:
5498  *         *  width = iconSize.cx + 16
5499  *         *    .left = position.x - (width - iconsize.cx)/2
5500  *         *    .right = .left + width
5501  *         *  height = iconSize.cy + 4
5502  *         *    .top = position.y - 2
5503  *         *    .bottom = .top + height
5504  *         *  separation between items .y = itemSpacing.cy - height
5505  *         *                           .x = itemSpacing.cx - width
5506  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5507  *         *
5508  *         * For LVS_ICON
5509  *         * Experiment shows that native control returns:
5510  *         *  width = text length
5511  *         *    .left = position.x - width/2
5512  *         *    .right = .left + width
5513  *         *  height = ntmH * linecount + 2
5514  *         *    .top = position.y + iconSize.cy + 6
5515  *         *    .bottom = .top + height
5516  *         *  separation between items .y = itemSpacing.cy - height
5517  *         *                           .x = itemSpacing.cx - width
5518  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5519  *      rectangles, but excludes columns in report view.
5520  *
5521  * RETURN:
5522  *   SUCCESS : TRUE
5523  *   FAILURE : FALSE
5524  *
5525  * NOTES
5526  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5527  *   upon whether the window has the focus currently and on whether the item
5528  *   is the one with the focus.  Ensure that the control's record of which
5529  *   item has the focus agrees with the items' records.
5530  */
5531 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5532 {
5533     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5534     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5535     BOOL doLabel = TRUE, oversizedBox = FALSE;
5536     POINT Position, Origin;
5537     LVITEMW lvItem;
5538     RECT label_rect;
5539
5540     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5541
5542     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5543
5544     LISTVIEW_GetOrigin(infoPtr, &Origin);
5545     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5546
5547     /* Be smart and try to figure out the minimum we have to do */
5548     if (lprc->left == LVIR_ICON) doLabel = FALSE;
5549     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5550     if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5551         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5552         oversizedBox = TRUE;
5553
5554     /* get what we need from the item before hand, so we make
5555      * only one request. This can speed up things, if data
5556      * is stored on the app side */
5557     lvItem.mask = 0;
5558     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5559     if (doLabel) lvItem.mask |= LVIF_TEXT;
5560     lvItem.iItem = nItem;
5561     lvItem.iSubItem = 0;
5562     lvItem.pszText = szDispText;
5563     lvItem.cchTextMax = DISP_TEXT_SIZE;
5564     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5565     /* we got the state already up, simulate it here, to avoid a reget */
5566     if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5567     {
5568         lvItem.mask |= LVIF_STATE;
5569         lvItem.stateMask = LVIS_FOCUSED;
5570         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5571     }
5572
5573     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5574         lprc->left = LVIR_BOUNDS;
5575     switch(lprc->left)
5576     {
5577     case LVIR_ICON:
5578         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5579         break;
5580
5581     case LVIR_LABEL:
5582         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5583         break;
5584
5585     case LVIR_BOUNDS:
5586         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5587         break;
5588
5589     case LVIR_SELECTBOUNDS:
5590         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5591         UnionRect(lprc, lprc, &label_rect);
5592         break;
5593
5594     default:
5595         WARN("Unknown value: %d\n", lprc->left);
5596         return FALSE;
5597     }
5598
5599     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5600
5601     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5602
5603     return TRUE;
5604 }
5605
5606 /***
5607  * DESCRIPTION:
5608  * Retrieves the spacing between listview control items.
5609  *
5610  * PARAMETER(S):
5611  * [I] infoPtr : valid pointer to the listview structure
5612  * [IO] lprc : rectangle to receive the output
5613  *             on input, lprc->top = nSubItem
5614  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5615  * 
5616  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5617  *       not only those of the first column.
5618  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5619  * 
5620  * RETURN:
5621  *     TRUE: success
5622  *     FALSE: failure
5623  */
5624 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5625 {
5626     POINT Position;
5627     LVITEMW lvItem;
5628     INT nColumn;
5629     
5630     if (!lprc) return FALSE;
5631
5632     nColumn = lprc->top;
5633
5634     TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5635     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5636     if (lprc->top == 0)
5637         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5638
5639     if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5640
5641     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5642
5643     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5644
5645     lvItem.mask = 0;
5646     lvItem.iItem = nItem;
5647     lvItem.iSubItem = nColumn;
5648     
5649     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5650     switch(lprc->left)
5651     {
5652     case LVIR_ICON:
5653         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5654         break;
5655
5656     case LVIR_LABEL:
5657     case LVIR_BOUNDS:
5658         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5659         break;
5660
5661     default:
5662         ERR("Unknown bounds=%d\n", lprc->left);
5663         return FALSE;
5664     }
5665
5666     OffsetRect(lprc, Position.x, Position.y);
5667     return TRUE;
5668 }
5669
5670
5671 /***
5672  * DESCRIPTION:
5673  * Retrieves the width of a label.
5674  *
5675  * PARAMETER(S):
5676  * [I] infoPtr : valid pointer to the listview structure
5677  *
5678  * RETURN:
5679  *   SUCCESS : string width (in pixels)
5680  *   FAILURE : zero
5681  */
5682 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5683 {
5684     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5685     LVITEMW lvItem;
5686
5687     TRACE("(nItem=%d)\n", nItem);
5688
5689     lvItem.mask = LVIF_TEXT;
5690     lvItem.iItem = nItem;
5691     lvItem.iSubItem = 0;
5692     lvItem.pszText = szDispText;
5693     lvItem.cchTextMax = DISP_TEXT_SIZE;
5694     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5695   
5696     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5697 }
5698
5699 /***
5700  * DESCRIPTION:
5701  * Retrieves the spacing between listview control items.
5702  *
5703  * PARAMETER(S):
5704  * [I] infoPtr : valid pointer to the listview structure
5705  * [I] bSmall : flag for small or large icon
5706  *
5707  * RETURN:
5708  * Horizontal + vertical spacing
5709  */
5710 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5711 {
5712   LONG lResult;
5713
5714   if (!bSmall)
5715   {
5716     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5717   }
5718   else
5719   {
5720     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5721       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5722     else
5723       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5724   }
5725   return lResult;
5726 }
5727
5728 /***
5729  * DESCRIPTION:
5730  * Retrieves the state of a listview control item.
5731  *
5732  * PARAMETER(S):
5733  * [I] infoPtr : valid pointer to the listview structure
5734  * [I] nItem : item index
5735  * [I] uMask : state mask
5736  *
5737  * RETURN:
5738  * State specified by the mask.
5739  */
5740 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5741 {
5742     LVITEMW lvItem;
5743
5744     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5745
5746     lvItem.iItem = nItem;
5747     lvItem.iSubItem = 0;
5748     lvItem.mask = LVIF_STATE;
5749     lvItem.stateMask = uMask;
5750     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5751
5752     return lvItem.state & uMask;
5753 }
5754
5755 /***
5756  * DESCRIPTION:
5757  * Retrieves the text of a listview control item or subitem.
5758  *
5759  * PARAMETER(S):
5760  * [I] hwnd : window handle
5761  * [I] nItem : item index
5762  * [IO] lpLVItem : item information
5763  * [I] isW :  TRUE if lpLVItem is Unicode
5764  *
5765  * RETURN:
5766  *   SUCCESS : string length
5767  *   FAILURE : 0
5768  */
5769 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5770 {
5771     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5772
5773     lpLVItem->mask = LVIF_TEXT;
5774     lpLVItem->iItem = nItem;
5775     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5776
5777     return textlenT(lpLVItem->pszText, isW);
5778 }
5779
5780 /***
5781  * DESCRIPTION:
5782  * Searches for an item based on properties + relationships.
5783  *
5784  * PARAMETER(S):
5785  * [I] infoPtr : valid pointer to the listview structure
5786  * [I] nItem : item index
5787  * [I] uFlags : relationship flag
5788  *
5789  * RETURN:
5790  *   SUCCESS : item index
5791  *   FAILURE : -1
5792  */
5793 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5794 {
5795     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5796     UINT uMask = 0;
5797     LVFINDINFOW lvFindInfo;
5798     INT nCountPerColumn;
5799     INT nCountPerRow;
5800     INT i;
5801
5802     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5803     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5804
5805     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5806
5807     if (uFlags & LVNI_CUT)
5808       uMask |= LVIS_CUT;
5809
5810     if (uFlags & LVNI_DROPHILITED)
5811       uMask |= LVIS_DROPHILITED;
5812
5813     if (uFlags & LVNI_FOCUSED)
5814       uMask |= LVIS_FOCUSED;
5815
5816     if (uFlags & LVNI_SELECTED)
5817       uMask |= LVIS_SELECTED;
5818
5819     /* if we're asked for the focused item, that's only one, 
5820      * so it's worth optimizing */
5821     if (uFlags & LVNI_FOCUSED)
5822     {
5823         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5824         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5825     }
5826     
5827     if (uFlags & LVNI_ABOVE)
5828     {
5829       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5830       {
5831         while (nItem >= 0)
5832         {
5833           nItem--;
5834           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5835             return nItem;
5836         }
5837       }
5838       else
5839       {
5840         /* Special case for autoarrange - move 'til the top of a list */
5841         if (is_autoarrange(infoPtr))
5842         {
5843           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5844           while (nItem - nCountPerRow >= 0)
5845           {
5846             nItem -= nCountPerRow;
5847             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5848               return nItem;
5849           }
5850           return -1;
5851         }
5852         lvFindInfo.flags = LVFI_NEARESTXY;
5853         lvFindInfo.vkDirection = VK_UP;
5854         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5855         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5856         {
5857           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5858             return nItem;
5859         }
5860       }
5861     }
5862     else if (uFlags & LVNI_BELOW)
5863     {
5864       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5865       {
5866         while (nItem < infoPtr->nItemCount)
5867         {
5868           nItem++;
5869           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5870             return nItem;
5871         }
5872       }
5873       else
5874       {
5875         /* Special case for autoarrange - move 'til the bottom of a list */
5876         if (is_autoarrange(infoPtr))
5877         {
5878           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5879           while (nItem + nCountPerRow < infoPtr->nItemCount )
5880           {
5881             nItem += nCountPerRow;
5882             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5883               return nItem;
5884           }
5885           return -1;
5886         }
5887         lvFindInfo.flags = LVFI_NEARESTXY;
5888         lvFindInfo.vkDirection = VK_DOWN;
5889         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5890         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5891         {
5892           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5893             return nItem;
5894         }
5895       }
5896     }
5897     else if (uFlags & LVNI_TOLEFT)
5898     {
5899       if (uView == LVS_LIST)
5900       {
5901         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5902         while (nItem - nCountPerColumn >= 0)
5903         {
5904           nItem -= nCountPerColumn;
5905           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5906             return nItem;
5907         }
5908       }
5909       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5910       {
5911         /* Special case for autoarrange - move 'ti the beginning of a row */
5912         if (is_autoarrange(infoPtr))
5913         {
5914           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5915           while (nItem % nCountPerRow > 0)
5916           {
5917             nItem --;
5918             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5919               return nItem;
5920           }
5921           return -1;
5922         }
5923         lvFindInfo.flags = LVFI_NEARESTXY;
5924         lvFindInfo.vkDirection = VK_LEFT;
5925         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5926         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5927         {
5928           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5929             return nItem;
5930         }
5931       }
5932     }
5933     else if (uFlags & LVNI_TORIGHT)
5934     {
5935       if (uView == LVS_LIST)
5936       {
5937         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5938         while (nItem + nCountPerColumn < infoPtr->nItemCount)
5939         {
5940           nItem += nCountPerColumn;
5941           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5942             return nItem;
5943         }
5944       }
5945       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5946       {
5947         /* Special case for autoarrange - move 'til the end of a row */
5948         if (is_autoarrange(infoPtr))
5949         {
5950           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5951           while (nItem % nCountPerRow < nCountPerRow - 1 )
5952           {
5953             nItem ++;
5954             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5955               return nItem;
5956           }
5957           return -1;
5958         }
5959         lvFindInfo.flags = LVFI_NEARESTXY;
5960         lvFindInfo.vkDirection = VK_RIGHT;
5961         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5962         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5963         {
5964           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5965             return nItem;
5966         }
5967       }
5968     }
5969     else
5970     {
5971       nItem++;
5972
5973       /* search by index */
5974       for (i = nItem; i < infoPtr->nItemCount; i++)
5975       {
5976         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5977           return i;
5978       }
5979     }
5980
5981     return -1;
5982 }
5983
5984 /* LISTVIEW_GetNumberOfWorkAreas */
5985
5986 /***
5987  * DESCRIPTION:
5988  * Retrieves the origin coordinates when in icon or small icon display mode.
5989  *
5990  * PARAMETER(S):
5991  * [I] infoPtr : valid pointer to the listview structure
5992  * [O] lpptOrigin : coordinate information
5993  *
5994  * RETURN:
5995  *   None.
5996  */
5997 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5998 {
5999     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6000     INT nHorzPos = 0, nVertPos = 0;
6001     SCROLLINFO scrollInfo;
6002
6003     scrollInfo.cbSize = sizeof(SCROLLINFO);    
6004     scrollInfo.fMask = SIF_POS;
6005     
6006     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6007         nHorzPos = scrollInfo.nPos;
6008     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6009         nVertPos = scrollInfo.nPos;
6010
6011     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6012
6013     lpptOrigin->x = infoPtr->rcList.left;
6014     lpptOrigin->y = infoPtr->rcList.top;
6015     if (uView == LVS_LIST)
6016         nHorzPos *= infoPtr->nItemWidth;
6017     else if (uView == LVS_REPORT)
6018         nVertPos *= infoPtr->nItemHeight;
6019     
6020     lpptOrigin->x -= nHorzPos;
6021     lpptOrigin->y -= nVertPos;
6022
6023     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6024 }
6025
6026 /***
6027  * DESCRIPTION:
6028  * Retrieves the width of a string.
6029  *
6030  * PARAMETER(S):
6031  * [I] hwnd : window handle
6032  * [I] lpszText : text string to process
6033  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6034  *
6035  * RETURN:
6036  *   SUCCESS : string width (in pixels)
6037  *   FAILURE : zero
6038  */
6039 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6040 {
6041     SIZE stringSize;
6042     
6043     stringSize.cx = 0;    
6044     if (is_textT(lpszText, isW))
6045     {
6046         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6047         HDC hdc = GetDC(infoPtr->hwndSelf);
6048         HFONT hOldFont = SelectObject(hdc, hFont);
6049
6050         if (isW)
6051             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6052         else
6053             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6054         SelectObject(hdc, hOldFont);
6055         ReleaseDC(infoPtr->hwndSelf, hdc);
6056     }
6057     return stringSize.cx;
6058 }
6059
6060 /***
6061  * DESCRIPTION:
6062  * Determines which listview item is located at the specified position.
6063  *
6064  * PARAMETER(S):
6065  * [I] infoPtr : valid pointer to the listview structure
6066  * [IO] lpht : hit test information
6067  * [I] subitem : fill out iSubItem.
6068  * [I] select : return the index only if the hit selects the item
6069  *
6070  * NOTE:
6071  * (mm 20001022): We must not allow iSubItem to be touched, for
6072  * an app might pass only a structure with space up to iItem!
6073  * (MS Office 97 does that for instance in the file open dialog)
6074  * 
6075  * RETURN:
6076  *   SUCCESS : item index
6077  *   FAILURE : -1
6078  */
6079 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6080 {
6081     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6082     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6083     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6084     POINT Origin, Position, opt;
6085     LVITEMW lvItem;
6086     ITERATOR i;
6087     INT iItem;
6088     
6089     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6090     
6091     lpht->flags = 0;
6092     lpht->iItem = -1;
6093     if (subitem) lpht->iSubItem = 0;
6094
6095     if (infoPtr->rcList.left > lpht->pt.x)
6096         lpht->flags |= LVHT_TOLEFT;
6097     else if (infoPtr->rcList.right < lpht->pt.x)
6098         lpht->flags |= LVHT_TORIGHT;
6099     
6100     if (infoPtr->rcList.top > lpht->pt.y)
6101         lpht->flags |= LVHT_ABOVE;
6102     else if (infoPtr->rcList.bottom < lpht->pt.y)
6103         lpht->flags |= LVHT_BELOW;
6104
6105     TRACE("lpht->flags=0x%x\n", lpht->flags);
6106     if (lpht->flags) return -1;
6107
6108     lpht->flags |= LVHT_NOWHERE;
6109
6110     LISTVIEW_GetOrigin(infoPtr, &Origin);
6111    
6112     /* first deal with the large items */
6113     rcSearch.left = lpht->pt.x;
6114     rcSearch.top = lpht->pt.y;
6115     rcSearch.right = rcSearch.left + 1;
6116     rcSearch.bottom = rcSearch.top + 1;
6117     
6118     iterator_frameditems(&i, infoPtr, &rcSearch);
6119     iterator_next(&i); /* go to first item in the sequence */
6120     iItem = i.nItem;
6121     iterator_destroy(&i);
6122    
6123     TRACE("lpht->iItem=%d\n", iItem); 
6124     if (iItem == -1) return -1;
6125
6126     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6127     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6128     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6129     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6130     lvItem.iItem = iItem;
6131     lvItem.iSubItem = 0;
6132     lvItem.pszText = szDispText;
6133     lvItem.cchTextMax = DISP_TEXT_SIZE;
6134     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6135     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED; 
6136     
6137     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6138     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6139     opt.x = lpht->pt.x - Position.x - Origin.x;
6140     opt.y = lpht->pt.y - Position.y - Origin.y;
6141     
6142     if (uView == LVS_REPORT)
6143         rcBounds = rcBox;
6144     else
6145         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6146     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6147     if (!PtInRect(&rcBounds, opt)) return -1;
6148
6149     if (PtInRect(&rcIcon, opt))
6150         lpht->flags |= LVHT_ONITEMICON;
6151     else if (PtInRect(&rcLabel, opt))
6152         lpht->flags |= LVHT_ONITEMLABEL;
6153     else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6154         lpht->flags |= LVHT_ONITEMSTATEICON;
6155     if (lpht->flags & LVHT_ONITEM)
6156         lpht->flags &= ~LVHT_NOWHERE;
6157    
6158     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6159     if (uView == LVS_REPORT && subitem)
6160     {
6161         INT j;
6162
6163         rcBounds.right = rcBounds.left;
6164         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6165         {
6166             rcBounds.left = rcBounds.right;
6167             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6168             if (PtInRect(&rcBounds, opt))
6169             {
6170                 lpht->iSubItem = j;
6171                 break;
6172             }
6173         }
6174     }
6175
6176     if (select && !(uView == LVS_REPORT &&
6177                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6178                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6179     {
6180         if (uView == LVS_REPORT)
6181         {
6182             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6183             UnionRect(&rcBounds, &rcBounds, &rcState);
6184         }
6185         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6186     }
6187     return lpht->iItem = iItem;
6188 }
6189
6190
6191 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6192    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6193    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6194    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6195    their own sort proc. when sending LVM_SORTITEMS.
6196 */
6197 /* Platform SDK:
6198     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6199         if:
6200           LVS_SORTXXX must be specified,
6201           LVS_OWNERDRAW is not set,
6202           <item>.pszText is not LPSTR_TEXTCALLBACK.
6203
6204     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6205     are sorted based on item text..."
6206 */
6207 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6208 {
6209     ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6210     ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6211     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
6212
6213     /* if we're sorting descending, negate the return value */
6214     return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6215 }
6216
6217 /***
6218  * DESCRIPTION:
6219  * Inserts a new item in the listview control.
6220  *
6221  * PARAMETER(S):
6222  * [I] infoPtr : valid pointer to the listview structure
6223  * [I] lpLVItem : item information
6224  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6225  *
6226  * RETURN:
6227  *   SUCCESS : new item index
6228  *   FAILURE : -1
6229  */
6230 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6231 {
6232     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6233     INT nItem;
6234     HDPA hdpaSubItems;
6235     NMLISTVIEW nmlv;
6236     ITEM_INFO *lpItem;
6237     BOOL is_sorted, has_changed;
6238     LVITEMW item;
6239     HWND hwndSelf = infoPtr->hwndSelf;
6240
6241     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6242
6243     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6244
6245     /* make sure it's an item, and not a subitem; cannot insert a subitem */
6246     if (!lpLVItem || lpLVItem->iSubItem) return -1;
6247
6248     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6249
6250     if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6251     
6252     /* insert item in listview control data structure */
6253     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6254     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6255
6256     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6257                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6258
6259     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6260
6261     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6262     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6263     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6264     if (nItem == -1) goto fail;
6265     infoPtr->nItemCount++;
6266
6267     /* shift indices first so they don't get tangled */
6268     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6269
6270     /* set the item attributes */
6271     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6272     {
6273         /* full size structure expected - _WIN32IE >= 0x560 */
6274         item = *lpLVItem;
6275     }
6276     else if (lpLVItem->mask & LVIF_INDENT)
6277     {
6278         /* indent member expected - _WIN32IE >= 0x300 */
6279         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6280     }
6281     else
6282     {
6283         /* minimal structure expected */
6284         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6285     }
6286     item.iItem = nItem;
6287     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6288     {
6289         item.mask |= LVIF_STATE;
6290         item.stateMask |= LVIS_STATEIMAGEMASK;
6291         item.state &= ~LVIS_STATEIMAGEMASK;
6292         item.state |= INDEXTOSTATEIMAGEMASK(1);
6293     }
6294     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6295
6296     /* if we're sorted, sort the list, and update the index */
6297     if (is_sorted)
6298     {
6299         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6300         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6301         assert(nItem != -1);
6302     }
6303
6304     /* make room for the position, if we are in the right mode */
6305     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6306     {
6307         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6308             goto undo;
6309         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6310         {
6311             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6312             goto undo;
6313         }
6314     }
6315     
6316     /* send LVN_INSERTITEM notification */
6317     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6318     nmlv.iItem = nItem;
6319     nmlv.lParam = lpItem->lParam;
6320     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6321     if (!IsWindow(hwndSelf))
6322         return -1;
6323
6324     /* align items (set position of each item) */
6325     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6326     {
6327         POINT pt;
6328
6329         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6330             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6331         else
6332             LISTVIEW_NextIconPosTop(infoPtr, &pt);
6333
6334         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6335     }
6336
6337     /* now is the invalidation fun */
6338     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6339     return nItem;
6340
6341 undo:
6342     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6343     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6344     infoPtr->nItemCount--;
6345 fail:
6346     DPA_DeletePtr(hdpaSubItems, 0);
6347     DPA_Destroy (hdpaSubItems);
6348     Free (lpItem);
6349     return -1;
6350 }
6351
6352 /***
6353  * DESCRIPTION:
6354  * Redraws a range of items.
6355  *
6356  * PARAMETER(S):
6357  * [I] infoPtr : valid pointer to the listview structure
6358  * [I] nFirst : first item
6359  * [I] nLast : last item
6360  *
6361  * RETURN:
6362  *   SUCCESS : TRUE
6363  *   FAILURE : FALSE
6364  */
6365 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6366 {
6367     INT i;
6368  
6369     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
6370         max(nFirst, nLast) >= infoPtr->nItemCount)
6371         return FALSE;
6372     
6373     for (i = nFirst; i <= nLast; i++)
6374         LISTVIEW_InvalidateItem(infoPtr, i);
6375
6376     return TRUE;
6377 }
6378
6379 /***
6380  * DESCRIPTION:
6381  * Scroll the content of a listview.
6382  *
6383  * PARAMETER(S):
6384  * [I] infoPtr : valid pointer to the listview structure
6385  * [I] dx : horizontal scroll amount in pixels
6386  * [I] dy : vertical scroll amount in pixels
6387  *
6388  * RETURN:
6389  *   SUCCESS : TRUE
6390  *   FAILURE : FALSE
6391  *
6392  * COMMENTS:
6393  *  If the control is in report mode (LVS_REPORT) the control can
6394  *  be scrolled only in line increments. "dy" will be rounded to the
6395  *  nearest number of pixels that are a whole line. Ex: if line height
6396  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6397  *  is passed the the scroll will be 0.  (per MSDN 7/2002)
6398  *
6399  *  For:  (per experimentaion with native control and CSpy ListView)
6400  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
6401  *                    dx ignored
6402  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
6403  *                    dx ignored
6404  *     LVS_LIST       dx=1 = 1 column (horizontal only)
6405  *                           but will only scroll 1 column per message
6406  *                           no matter what the value.
6407  *                    dy must be 0 or FALSE returned.
6408  *     LVS_REPORT     dx=1 = 1 pixel
6409  *                    dy=  see above
6410  *
6411  */
6412 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6413 {
6414     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6415     case LVS_REPORT:
6416         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6417         dy /= infoPtr->nItemHeight;
6418         break;
6419     case LVS_LIST:
6420         if (dy != 0) return FALSE;
6421         break;
6422     default: /* icon */
6423         dx = 0;
6424         break;
6425     }   
6426
6427     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6428     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6429   
6430     return TRUE;
6431 }
6432
6433 /***
6434  * DESCRIPTION:
6435  * Sets the background color.
6436  *
6437  * PARAMETER(S):
6438  * [I] infoPtr : valid pointer to the listview structure
6439  * [I] clrBk : background color
6440  *
6441  * RETURN:
6442  *   SUCCESS : TRUE
6443  *   FAILURE : FALSE
6444  */
6445 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6446 {
6447     TRACE("(clrBk=%x)\n", clrBk);
6448
6449     if(infoPtr->clrBk != clrBk) {
6450         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6451         infoPtr->clrBk = clrBk;
6452         if (clrBk == CLR_NONE)
6453             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6454         else
6455             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6456         LISTVIEW_InvalidateList(infoPtr);
6457     }
6458
6459    return TRUE;
6460 }
6461
6462 /* LISTVIEW_SetBkImage */
6463
6464 /*** Helper for {Insert,Set}ColumnT *only* */
6465 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6466 {
6467     if (lpColumn->mask & LVCF_FMT)
6468     {
6469         /* format member is valid */
6470         lphdi->mask |= HDI_FORMAT;
6471
6472         /* set text alignment (leftmost column must be left-aligned) */
6473         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6474             lphdi->fmt |= HDF_LEFT;
6475         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6476             lphdi->fmt |= HDF_RIGHT;
6477         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6478             lphdi->fmt |= HDF_CENTER;
6479
6480         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6481             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6482
6483         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6484         {
6485             lphdi->fmt |= HDF_IMAGE;
6486             lphdi->iImage = I_IMAGECALLBACK;
6487         }
6488     }
6489
6490     if (lpColumn->mask & LVCF_WIDTH)
6491     {
6492         lphdi->mask |= HDI_WIDTH;
6493         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6494         {
6495             /* make it fill the remainder of the controls width */
6496             RECT rcHeader;
6497             INT item_index;
6498
6499             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6500             {
6501                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6502                 lphdi->cxy += rcHeader.right - rcHeader.left;
6503             }
6504
6505             /* retrieve the layout of the header */
6506             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6507             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6508
6509             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6510         }
6511         else
6512             lphdi->cxy = lpColumn->cx;
6513     }
6514
6515     if (lpColumn->mask & LVCF_TEXT)
6516     {
6517         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6518         lphdi->fmt |= HDF_STRING;
6519         lphdi->pszText = lpColumn->pszText;
6520         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6521     }
6522
6523     if (lpColumn->mask & LVCF_IMAGE)
6524     {
6525         lphdi->mask |= HDI_IMAGE;
6526         lphdi->iImage = lpColumn->iImage;
6527     }
6528
6529     if (lpColumn->mask & LVCF_ORDER)
6530     {
6531         lphdi->mask |= HDI_ORDER;
6532         lphdi->iOrder = lpColumn->iOrder;
6533     }
6534 }
6535
6536
6537 /***
6538  * DESCRIPTION:
6539  * Inserts a new column.
6540  *
6541  * PARAMETER(S):
6542  * [I] infoPtr : valid pointer to the listview structure
6543  * [I] nColumn : column index
6544  * [I] lpColumn : column information
6545  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6546  *
6547  * RETURN:
6548  *   SUCCESS : new column index
6549  *   FAILURE : -1
6550  */
6551 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6552                                   const LVCOLUMNW *lpColumn, BOOL isW)
6553 {
6554     COLUMN_INFO *lpColumnInfo;
6555     INT nNewColumn;
6556     HDITEMW hdi;
6557
6558     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6559
6560     if (!lpColumn || nColumn < 0) return -1;
6561     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6562     
6563     ZeroMemory(&hdi, sizeof(HDITEMW));
6564     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6565     
6566     /*
6567      * when the iSubItem is available Windows copies it to the header lParam. It seems
6568      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6569      */
6570     if (lpColumn->mask & LVCF_SUBITEM)
6571     {
6572         hdi.mask |= HDI_LPARAM;
6573         hdi.lParam = lpColumn->iSubItem;
6574     }
6575
6576     /* insert item in header control */
6577     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6578                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6579                               (WPARAM)nColumn, (LPARAM)&hdi);
6580     if (nNewColumn == -1) return -1;
6581     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6582    
6583     /* create our own column info */ 
6584     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6585     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6586
6587     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6588     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6589
6590     /* now we have to actually adjust the data */
6591     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6592     {
6593         SUBITEM_INFO *lpSubItem;
6594         HDPA hdpaSubItems;
6595         INT nItem, i;
6596         
6597         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6598         {
6599             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6600             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6601             {
6602                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6603                 if (lpSubItem->iSubItem >= nNewColumn)
6604                     lpSubItem->iSubItem++;
6605             }
6606         }
6607     }
6608
6609     /* make space for the new column */
6610     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6611     LISTVIEW_UpdateItemSize(infoPtr);
6612     
6613     return nNewColumn;
6614
6615 fail:
6616     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6617     if (lpColumnInfo)
6618     {
6619         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6620         Free(lpColumnInfo);
6621     }
6622     return -1;
6623 }
6624
6625 /***
6626  * DESCRIPTION:
6627  * Sets the attributes of a header item.
6628  *
6629  * PARAMETER(S):
6630  * [I] infoPtr : valid pointer to the listview structure
6631  * [I] nColumn : column index
6632  * [I] lpColumn : column attributes
6633  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6634  *
6635  * RETURN:
6636  *   SUCCESS : TRUE
6637  *   FAILURE : FALSE
6638  */
6639 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6640                                 const LVCOLUMNW *lpColumn, BOOL isW)
6641 {
6642     HDITEMW hdi, hdiget;
6643     BOOL bResult;
6644
6645     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6646     
6647     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6648
6649     ZeroMemory(&hdi, sizeof(HDITEMW));
6650     if (lpColumn->mask & LVCF_FMT)
6651     {
6652         hdi.mask |= HDI_FORMAT;
6653         hdiget.mask = HDI_FORMAT;
6654         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6655             hdi.fmt = hdiget.fmt & HDF_STRING;
6656     }
6657     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6658
6659     /* set header item attributes */
6660     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6661     if (!bResult) return FALSE;
6662
6663     if (lpColumn->mask & LVCF_FMT)
6664     {
6665         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6666         int oldFmt = lpColumnInfo->fmt;
6667         
6668         lpColumnInfo->fmt = lpColumn->fmt;
6669         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6670         {
6671             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6672             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6673         }
6674     }
6675
6676     return TRUE;
6677 }
6678
6679 /***
6680  * DESCRIPTION:
6681  * Sets the column order array
6682  *
6683  * PARAMETERS:
6684  * [I] infoPtr : valid pointer to the listview structure
6685  * [I] iCount : number of elements in column order array
6686  * [I] lpiArray : pointer to column order array
6687  *
6688  * RETURN:
6689  *   SUCCESS : TRUE
6690  *   FAILURE : FALSE
6691  */
6692 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6693 {
6694   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6695
6696   if (!lpiArray)
6697     return FALSE;
6698
6699   return TRUE;
6700 }
6701
6702
6703 /***
6704  * DESCRIPTION:
6705  * Sets the width of a column
6706  *
6707  * PARAMETERS:
6708  * [I] infoPtr : valid pointer to the listview structure
6709  * [I] nColumn : column index
6710  * [I] cx : column width
6711  *
6712  * RETURN:
6713  *   SUCCESS : TRUE
6714  *   FAILURE : FALSE
6715  */
6716 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6717 {
6718     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6719     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6720     INT max_cx = 0;
6721     HDITEMW hdi;
6722
6723     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6724
6725     /* set column width only if in report or list mode */
6726     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6727
6728     /* take care of invalid cx values */
6729     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6730     else if (uView == LVS_LIST && cx < 1) return FALSE;
6731
6732     /* resize all columns if in LVS_LIST mode */
6733     if(uView == LVS_LIST) 
6734     {
6735         infoPtr->nItemWidth = cx;
6736         LISTVIEW_InvalidateList(infoPtr);
6737         return TRUE;
6738     }
6739
6740     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6741
6742     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6743     {
6744         INT nLabelWidth;
6745         LVITEMW lvItem;
6746
6747         lvItem.mask = LVIF_TEXT;        
6748         lvItem.iItem = 0;
6749         lvItem.iSubItem = nColumn;
6750         lvItem.pszText = szDispText;
6751         lvItem.cchTextMax = DISP_TEXT_SIZE;
6752         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6753         {
6754             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6755             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6756             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6757         }
6758         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6759             max_cx += infoPtr->iconSize.cx;
6760         max_cx += TRAILING_LABEL_PADDING;
6761     }
6762
6763     /* autosize based on listview items width */
6764     if(cx == LVSCW_AUTOSIZE)
6765         cx = max_cx;
6766     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6767     {
6768         /* if iCol is the last column make it fill the remainder of the controls width */
6769         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
6770         {
6771             RECT rcHeader;
6772             POINT Origin;
6773
6774             LISTVIEW_GetOrigin(infoPtr, &Origin);
6775             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6776
6777             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6778         }
6779         else
6780         {
6781             /* Despite what the MS docs say, if this is not the last
6782                column, then MS resizes the column to the width of the
6783                largest text string in the column, including headers
6784                and items. This is different from LVSCW_AUTOSIZE in that
6785                LVSCW_AUTOSIZE ignores the header string length. */
6786             cx = 0;
6787
6788             /* retrieve header text */
6789             hdi.mask = HDI_TEXT;
6790             hdi.cchTextMax = DISP_TEXT_SIZE;
6791             hdi.pszText = szDispText;
6792             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6793             {
6794                 HDC hdc = GetDC(infoPtr->hwndSelf);
6795                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6796                 SIZE size;
6797
6798                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6799                     cx = size.cx + TRAILING_HEADER_PADDING;
6800                 /* FIXME: Take into account the header image, if one is present */
6801                 SelectObject(hdc, old_font);
6802                 ReleaseDC(infoPtr->hwndSelf, hdc);
6803             }
6804             cx = max (cx, max_cx);
6805         }
6806     }
6807
6808     if (cx < 0) return FALSE;
6809
6810     /* call header to update the column change */
6811     hdi.mask = HDI_WIDTH;
6812     hdi.cxy = cx;
6813     TRACE("hdi.cxy=%d\n", hdi.cxy);
6814     return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6815 }
6816
6817 /***
6818  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
6819  *
6820  */
6821 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6822 {
6823     HDC hdc_wnd, hdc;
6824     HBITMAP hbm_im, hbm_mask, hbm_orig;
6825     RECT rc;
6826     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6827     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6828     HIMAGELIST himl;
6829
6830     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6831                             ILC_COLOR | ILC_MASK, 2, 2);
6832     hdc_wnd = GetDC(infoPtr->hwndSelf);
6833     hdc = CreateCompatibleDC(hdc_wnd);
6834     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6835     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6836     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6837
6838     rc.left = rc.top = 0;
6839     rc.right = GetSystemMetrics(SM_CXSMICON);
6840     rc.bottom = GetSystemMetrics(SM_CYSMICON);
6841
6842     hbm_orig = SelectObject(hdc, hbm_mask);
6843     FillRect(hdc, &rc, hbr_white);
6844     InflateRect(&rc, -3, -3);
6845     FillRect(hdc, &rc, hbr_black);
6846
6847     SelectObject(hdc, hbm_im);
6848     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6849     SelectObject(hdc, hbm_orig);
6850     ImageList_Add(himl, hbm_im, hbm_mask); 
6851
6852     SelectObject(hdc, hbm_im);
6853     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6854     SelectObject(hdc, hbm_orig);
6855     ImageList_Add(himl, hbm_im, hbm_mask);
6856
6857     DeleteObject(hbm_mask);
6858     DeleteObject(hbm_im);
6859     DeleteDC(hdc);
6860
6861     return himl;
6862 }
6863
6864 /***
6865  * DESCRIPTION:
6866  * Sets the extended listview style.
6867  *
6868  * PARAMETERS:
6869  * [I] infoPtr : valid pointer to the listview structure
6870  * [I] dwMask : mask
6871  * [I] dwStyle : style
6872  *
6873  * RETURN:
6874  *   SUCCESS : previous style
6875  *   FAILURE : 0
6876  */
6877 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6878 {
6879     DWORD dwOldStyle = infoPtr->dwLvExStyle;
6880
6881     /* set new style */
6882     if (dwMask)
6883         infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6884     else
6885         infoPtr->dwLvExStyle = dwStyle;
6886
6887     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6888     {
6889         HIMAGELIST himl = 0;
6890         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6891         {
6892             LVITEMW item;
6893             item.mask = LVIF_STATE;
6894             item.stateMask = LVIS_STATEIMAGEMASK;
6895             item.state = INDEXTOSTATEIMAGEMASK(1);
6896             LISTVIEW_SetItemState(infoPtr, -1, &item);
6897
6898             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6899         }
6900         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6901     }
6902     
6903     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_HEADERDRAGDROP)
6904     {
6905         DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
6906         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
6907             dwStyle |= HDS_DRAGDROP;
6908         else
6909             dwStyle &= ~HDS_DRAGDROP;
6910         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
6911     }
6912
6913     return dwOldStyle;
6914 }
6915
6916 /***
6917  * DESCRIPTION:
6918  * Sets the new hot cursor used during hot tracking and hover selection.
6919  *
6920  * PARAMETER(S):
6921  * [I] infoPtr : valid pointer to the listview structure
6922  * [I] hCursor : the new hot cursor handle
6923  *
6924  * RETURN:
6925  * Returns the previous hot cursor
6926  */
6927 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6928 {
6929     HCURSOR oldCursor = infoPtr->hHotCursor;
6930     
6931     infoPtr->hHotCursor = hCursor;
6932
6933     return oldCursor;
6934 }
6935
6936
6937 /***
6938  * DESCRIPTION:
6939  * Sets the hot item index.
6940  *
6941  * PARAMETERS:
6942  * [I] infoPtr : valid pointer to the listview structure
6943  * [I] iIndex : index
6944  *
6945  * RETURN:
6946  *   SUCCESS : previous hot item index
6947  *   FAILURE : -1 (no hot item)
6948  */
6949 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6950 {
6951     INT iOldIndex = infoPtr->nHotItem;
6952     
6953     infoPtr->nHotItem = iIndex;
6954     
6955     return iOldIndex;
6956 }
6957
6958
6959 /***
6960  * DESCRIPTION:
6961  * Sets the amount of time the cursor must hover over an item before it is selected.
6962  *
6963  * PARAMETER(S):
6964  * [I] infoPtr : valid pointer to the listview structure
6965  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6966  *
6967  * RETURN:
6968  * Returns the previous hover time
6969  */
6970 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6971 {
6972     DWORD oldHoverTime = infoPtr->dwHoverTime;
6973     
6974     infoPtr->dwHoverTime = dwHoverTime;
6975     
6976     return oldHoverTime;
6977 }
6978
6979 /***
6980  * DESCRIPTION:
6981  * Sets spacing for icons of LVS_ICON style.
6982  *
6983  * PARAMETER(S):
6984  * [I] infoPtr : valid pointer to the listview structure
6985  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6986  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6987  *
6988  * RETURN:
6989  *   MAKELONG(oldcx, oldcy)
6990  */
6991 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6992 {
6993     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6994     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6995
6996     TRACE("requested=(%d,%d)\n", cx, cy);
6997     
6998     /* this is supported only for LVS_ICON style */
6999     if (uView != LVS_ICON) return oldspacing;
7000   
7001     /* set to defaults, if instructed to */
7002     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7003     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7004
7005     /* if 0 then compute width
7006      * FIXME: Should scan each item and determine max width of
7007      *        icon or label, then make that the width */
7008     if (cx == 0)
7009         cx = infoPtr->iconSpacing.cx;
7010
7011     /* if 0 then compute height */
7012     if (cy == 0) 
7013         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7014              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7015     
7016
7017     infoPtr->iconSpacing.cx = cx;
7018     infoPtr->iconSpacing.cy = cy;
7019
7020     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7021           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
7022           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7023           infoPtr->ntmHeight);
7024
7025     /* these depend on the iconSpacing */
7026     LISTVIEW_UpdateItemSize(infoPtr);
7027
7028     return oldspacing;
7029 }
7030
7031 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7032 {
7033     INT cx, cy;
7034     
7035     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7036     {
7037         size->cx = cx;
7038         size->cy = cy;
7039     }
7040     else
7041     {
7042         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7043         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7044     }
7045 }
7046
7047 /***
7048  * DESCRIPTION:
7049  * Sets image lists.
7050  *
7051  * PARAMETER(S):
7052  * [I] infoPtr : valid pointer to the listview structure
7053  * [I] nType : image list type
7054  * [I] himl : image list handle
7055  *
7056  * RETURN:
7057  *   SUCCESS : old image list
7058  *   FAILURE : NULL
7059  */
7060 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7061 {
7062     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7063     INT oldHeight = infoPtr->nItemHeight;
7064     HIMAGELIST himlOld = 0;
7065
7066     TRACE("(nType=%d, himl=%p\n", nType, himl);
7067
7068     switch (nType)
7069     {
7070     case LVSIL_NORMAL:
7071         himlOld = infoPtr->himlNormal;
7072         infoPtr->himlNormal = himl;
7073         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7074         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7075     break;
7076
7077     case LVSIL_SMALL:
7078         himlOld = infoPtr->himlSmall;
7079         infoPtr->himlSmall = himl;
7080          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7081     break;
7082
7083     case LVSIL_STATE:
7084         himlOld = infoPtr->himlState;
7085         infoPtr->himlState = himl;
7086         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7087         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7088     break;
7089
7090     default:
7091         ERR("Unknown icon type=%d\n", nType);
7092         return NULL;
7093     }
7094
7095     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7096     if (infoPtr->nItemHeight != oldHeight)
7097         LISTVIEW_UpdateScroll(infoPtr);
7098
7099     return himlOld;
7100 }
7101
7102 /***
7103  * DESCRIPTION:
7104  * Preallocates memory (does *not* set the actual count of items !)
7105  *
7106  * PARAMETER(S):
7107  * [I] infoPtr : valid pointer to the listview structure
7108  * [I] nItems : item count (projected number of items to allocate)
7109  * [I] dwFlags : update flags
7110  *
7111  * RETURN:
7112  *   SUCCESS : TRUE
7113  *   FAILURE : FALSE
7114  */
7115 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7116 {
7117     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7118
7119     if (infoPtr->dwStyle & LVS_OWNERDATA)
7120     {
7121         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7122         INT nOldCount = infoPtr->nItemCount;
7123
7124         if (nItems < nOldCount)
7125         {
7126             RANGE range = { nItems, nOldCount };
7127             ranges_del(infoPtr->selectionRanges, range);
7128             if (infoPtr->nFocusedItem >= nItems)
7129             {
7130                 infoPtr->nFocusedItem = -1;
7131                 SetRectEmpty(&infoPtr->rcFocus);
7132             }
7133         }
7134
7135         infoPtr->nItemCount = nItems;
7136         LISTVIEW_UpdateScroll(infoPtr);
7137
7138         /* the flags are valid only in ownerdata report and list modes */
7139         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7140
7141         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7142             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7143
7144         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7145             LISTVIEW_InvalidateList(infoPtr);
7146         else
7147         {
7148             INT nFrom, nTo;
7149             POINT Origin;
7150             RECT rcErase;
7151             
7152             LISTVIEW_GetOrigin(infoPtr, &Origin);
7153             nFrom = min(nOldCount, nItems);
7154             nTo = max(nOldCount, nItems);
7155     
7156             if (uView == LVS_REPORT)
7157             {
7158                 rcErase.left = 0;
7159                 rcErase.top = nFrom * infoPtr->nItemHeight;
7160                 rcErase.right = infoPtr->nItemWidth;
7161                 rcErase.bottom = nTo * infoPtr->nItemHeight;
7162                 OffsetRect(&rcErase, Origin.x, Origin.y);
7163                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7164                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7165             }
7166             else /* LVS_LIST */
7167             {
7168                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7169
7170                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7171                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7172                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7173                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7174                 OffsetRect(&rcErase, Origin.x, Origin.y);
7175                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7176                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7177
7178                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7179                 rcErase.top = 0;
7180                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7181                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7182                 OffsetRect(&rcErase, Origin.x, Origin.y);
7183                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7184                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7185             }
7186         }
7187     }
7188     else
7189     {
7190         /* According to MSDN for non-LVS_OWNERDATA this is just
7191          * a performance issue. The control allocates its internal
7192          * data structures for the number of items specified. It
7193          * cuts down on the number of memory allocations. Therefore
7194          * we will just issue a WARN here
7195          */
7196         WARN("for non-ownerdata performance option not implemented.\n");
7197     }
7198
7199     return TRUE;
7200 }
7201
7202 /***
7203  * DESCRIPTION:
7204  * Sets the position of an item.
7205  *
7206  * PARAMETER(S):
7207  * [I] infoPtr : valid pointer to the listview structure
7208  * [I] nItem : item index
7209  * [I] pt : coordinate
7210  *
7211  * RETURN:
7212  *   SUCCESS : TRUE
7213  *   FAILURE : FALSE
7214  */
7215 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7216 {
7217     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7218     POINT Origin;
7219
7220     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7221
7222     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7223         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7224
7225     LISTVIEW_GetOrigin(infoPtr, &Origin);
7226
7227     /* This point value seems to be an undocumented feature.
7228      * The best guess is that it means either at the origin, 
7229      * or at true beginning of the list. I will assume the origin. */
7230     if ((pt.x == -1) && (pt.y == -1))
7231         pt = Origin;
7232     
7233     if (uView == LVS_ICON)
7234     {
7235         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7236         pt.y -= ICON_TOP_PADDING;
7237     }
7238     pt.x -= Origin.x;
7239     pt.y -= Origin.y;
7240
7241     infoPtr->bAutoarrange = FALSE;
7242
7243     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7244 }
7245
7246 /***
7247  * DESCRIPTION:
7248  * Sets the state of one or many items.
7249  *
7250  * PARAMETER(S):
7251  * [I] infoPtr : valid pointer to the listview structure
7252  * [I] nItem : item index
7253  * [I] lpLVItem : item or subitem info
7254  *
7255  * RETURN:
7256  *   SUCCESS : TRUE
7257  *   FAILURE : FALSE
7258  */
7259 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7260 {
7261     BOOL bResult = TRUE;
7262     LVITEMW lvItem;
7263
7264     lvItem.iItem = nItem;
7265     lvItem.iSubItem = 0;
7266     lvItem.mask = LVIF_STATE;
7267     lvItem.state = lpLVItem->state;
7268     lvItem.stateMask = lpLVItem->stateMask;
7269     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7270
7271     if (nItem == -1)
7272     {
7273         /* apply to all items */
7274         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7275             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7276     }
7277     else
7278         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7279
7280     /*
7281      * Update selection mark
7282      *
7283      * Investigation on windows 2k showed that selection mark was updated
7284      * whenever a new selection was made, but if the selected item was
7285      * unselected it was not updated.
7286      *
7287      * we are probably still not 100% accurate, but this at least sets the
7288      * proper selection mark when it is needed
7289      */
7290
7291     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7292         ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7293     {
7294         int i;
7295         infoPtr->nSelectionMark = -1;
7296         for (i = 0; i < infoPtr->nItemCount; i++)
7297         {
7298             if (infoPtr->uCallbackMask & LVIS_SELECTED)
7299             {
7300                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7301                 {
7302                     infoPtr->nSelectionMark = i;
7303                     break;
7304                 }
7305             }
7306             else if (ranges_contain(infoPtr->selectionRanges, i))
7307             {
7308                 infoPtr->nSelectionMark = i;
7309                 break;
7310             }
7311         }
7312     }
7313
7314     return bResult;
7315 }
7316
7317 /***
7318  * DESCRIPTION:
7319  * Sets the text of an item or subitem.
7320  *
7321  * PARAMETER(S):
7322  * [I] hwnd : window handle
7323  * [I] nItem : item index
7324  * [I] lpLVItem : item or subitem info
7325  * [I] isW : TRUE if input is Unicode
7326  *
7327  * RETURN:
7328  *   SUCCESS : TRUE
7329  *   FAILURE : FALSE
7330  */
7331 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7332 {
7333     LVITEMW lvItem;
7334
7335     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7336     
7337     lvItem.iItem = nItem;
7338     lvItem.iSubItem = lpLVItem->iSubItem;
7339     lvItem.mask = LVIF_TEXT;
7340     lvItem.pszText = lpLVItem->pszText;
7341     lvItem.cchTextMax = lpLVItem->cchTextMax;
7342     
7343     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7344
7345     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
7346 }
7347
7348 /***
7349  * DESCRIPTION:
7350  * Set item index that marks the start of a multiple selection.
7351  *
7352  * PARAMETER(S):
7353  * [I] infoPtr : valid pointer to the listview structure
7354  * [I] nIndex : index
7355  *
7356  * RETURN:
7357  * Index number or -1 if there is no selection mark.
7358  */
7359 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7360 {
7361   INT nOldIndex = infoPtr->nSelectionMark;
7362
7363   TRACE("(nIndex=%d)\n", nIndex);
7364
7365   infoPtr->nSelectionMark = nIndex;
7366
7367   return nOldIndex;
7368 }
7369
7370 /***
7371  * DESCRIPTION:
7372  * Sets the text background color.
7373  *
7374  * PARAMETER(S):
7375  * [I] infoPtr : valid pointer to the listview structure
7376  * [I] clrTextBk : text background color
7377  *
7378  * RETURN:
7379  *   SUCCESS : TRUE
7380  *   FAILURE : FALSE
7381  */
7382 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7383 {
7384     TRACE("(clrTextBk=%x)\n", clrTextBk);
7385
7386     if (infoPtr->clrTextBk != clrTextBk)
7387     {
7388         infoPtr->clrTextBk = clrTextBk;
7389         LISTVIEW_InvalidateList(infoPtr);
7390     }
7391     
7392   return TRUE;
7393 }
7394
7395 /***
7396  * DESCRIPTION:
7397  * Sets the text foreground color.
7398  *
7399  * PARAMETER(S):
7400  * [I] infoPtr : valid pointer to the listview structure
7401  * [I] clrText : text color
7402  *
7403  * RETURN:
7404  *   SUCCESS : TRUE
7405  *   FAILURE : FALSE
7406  */
7407 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7408 {
7409     TRACE("(clrText=%x)\n", clrText);
7410
7411     if (infoPtr->clrText != clrText)
7412     {
7413         infoPtr->clrText = clrText;
7414         LISTVIEW_InvalidateList(infoPtr);
7415     }
7416
7417     return TRUE;
7418 }
7419
7420 /***
7421  * DESCRIPTION:
7422  * Determines which listview item is located at the specified position.
7423  *
7424  * PARAMETER(S):
7425  * [I] infoPtr        : valid pointer to the listview structure
7426  * [I] hwndNewToolTip : handle to new ToolTip
7427  *
7428  * RETURN:
7429  *   old tool tip
7430  */
7431 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7432 {
7433   HWND hwndOldToolTip = infoPtr->hwndToolTip;
7434   infoPtr->hwndToolTip = hwndNewToolTip;
7435   return hwndOldToolTip;
7436 }
7437
7438 /* LISTVIEW_SetUnicodeFormat */
7439 /* LISTVIEW_SetWorkAreas */
7440
7441 /***
7442  * DESCRIPTION:
7443  * Callback internally used by LISTVIEW_SortItems()
7444  *
7445  * PARAMETER(S):
7446  * [I] first : pointer to first ITEM_INFO to compare
7447  * [I] second : pointer to second ITEM_INFO to compare
7448  * [I] lParam : HWND of control
7449  *
7450  * RETURN:
7451  *   if first comes before second : negative
7452  *   if first comes after second : positive
7453  *   if first and second are equivalent : zero
7454  */
7455 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7456 {
7457   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7458   ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7459   ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7460
7461   /* Forward the call to the client defined callback */
7462   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7463 }
7464
7465 /***
7466  * DESCRIPTION:
7467  * Sorts the listview items.
7468  *
7469  * PARAMETER(S):
7470  * [I] infoPtr : valid pointer to the listview structure
7471  * [I] pfnCompare : application-defined value
7472  * [I] lParamSort : pointer to comparision callback
7473  *
7474  * RETURN:
7475  *   SUCCESS : TRUE
7476  *   FAILURE : FALSE
7477  */
7478 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7479 {
7480     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7481     HDPA hdpaSubItems;
7482     ITEM_INFO *lpItem;
7483     LPVOID selectionMarkItem;
7484     LVITEMW item;
7485     int i;
7486
7487     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7488
7489     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7490
7491     if (!pfnCompare) return FALSE;
7492     if (!infoPtr->hdpaItems) return FALSE;
7493
7494     /* if there are 0 or 1 items, there is no need to sort */
7495     if (infoPtr->nItemCount < 2) return TRUE;
7496
7497     if (infoPtr->nFocusedItem >= 0)
7498     {
7499         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7500         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7501         if (lpItem) lpItem->state |= LVIS_FOCUSED;
7502     }
7503     /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7504     /*        clear the lpItem->state for non-selected ones */
7505     /*        remove the selection ranges */
7506     
7507     infoPtr->pfnCompare = pfnCompare;
7508     infoPtr->lParamSort = lParamSort;
7509     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7510
7511     /* Adjust selections and indices so that they are the way they should
7512      * be after the sort (otherwise, the list items move around, but
7513      * whatever is at the item's previous original position will be
7514      * selected instead)
7515      */
7516     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7517     for (i=0; i < infoPtr->nItemCount; i++)
7518     {
7519         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7520         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7521
7522         if (lpItem->state & LVIS_SELECTED)
7523         {
7524             item.state = LVIS_SELECTED;
7525             item.stateMask = LVIS_SELECTED;
7526             LISTVIEW_SetItemState(infoPtr, i, &item);
7527         }
7528         if (lpItem->state & LVIS_FOCUSED)
7529         {
7530             infoPtr->nFocusedItem = i;
7531             lpItem->state &= ~LVIS_FOCUSED;
7532         }
7533     }
7534     if (selectionMarkItem != NULL)
7535         infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7536     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7537
7538     /* refresh the display */
7539     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7540         LISTVIEW_InvalidateList(infoPtr);
7541
7542     return TRUE;
7543 }
7544
7545 /***
7546  * DESCRIPTION:
7547  * Update theme handle after a theme change.
7548  *
7549  * PARAMETER(S):
7550  * [I] infoPtr : valid pointer to the listview structure
7551  *
7552  * RETURN:
7553  *   SUCCESS : 0
7554  *   FAILURE : something else
7555  */
7556 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7557 {
7558     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7559     CloseThemeData(theme);
7560     OpenThemeData(infoPtr->hwndSelf, themeClass);
7561     return 0;
7562 }
7563
7564 /***
7565  * DESCRIPTION:
7566  * Updates an items or rearranges the listview control.
7567  *
7568  * PARAMETER(S):
7569  * [I] infoPtr : valid pointer to the listview structure
7570  * [I] nItem : item index
7571  *
7572  * RETURN:
7573  *   SUCCESS : TRUE
7574  *   FAILURE : FALSE
7575  */
7576 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7577 {
7578     TRACE("(nItem=%d)\n", nItem);
7579
7580     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7581
7582     /* rearrange with default alignment style */
7583     if (is_autoarrange(infoPtr))
7584         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7585     else
7586         LISTVIEW_InvalidateItem(infoPtr, nItem);
7587
7588     return TRUE;
7589 }
7590
7591 /***
7592  * DESCRIPTION:
7593  * Draw the track line at the place defined in the infoPtr structure.
7594  * The line is drawn with a XOR pen so drawing the line for the second time
7595  * in the same place erases the line.
7596  *
7597  * PARAMETER(S):
7598  * [I] infoPtr : valid pointer to the listview structure
7599  *
7600  * RETURN:
7601  *   SUCCESS : TRUE
7602  *   FAILURE : FALSE
7603  */
7604 static BOOL LISTVIEW_DrawTrackLine(LISTVIEW_INFO *infoPtr)
7605 {
7606     HPEN hOldPen;
7607     HDC hdc;
7608     INT oldROP;
7609
7610     if (infoPtr->xTrackLine == -1)
7611         return FALSE;
7612
7613     if (!(hdc = GetDC(infoPtr->hwndSelf)))
7614         return FALSE;
7615     hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7616     oldROP = SetROP2(hdc, R2_XORPEN);
7617     MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7618     LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7619     SetROP2(hdc, oldROP);
7620     SelectObject(hdc, hOldPen);
7621     ReleaseDC(infoPtr->hwndSelf, hdc);
7622     return TRUE;
7623 }
7624
7625 /***
7626  * DESCRIPTION:
7627  * Called when an edit control should be displayed. This function is called after
7628  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7629  *
7630  * PARAMETER(S):
7631  * [I] hwnd : Handle to the listview
7632  * [I] uMsg : WM_TIMER (ignored)
7633  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7634  * [I] dwTimer : The elapsed time (ignored)
7635  *
7636  * RETURN:
7637  *   None.
7638  */
7639 static CALLBACK VOID LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7640 {
7641     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7642     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7643
7644     KillTimer(hwnd, idEvent);
7645     editItem->fEnabled = FALSE;
7646     /* check if the item is still selected */
7647     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7648         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7649 }
7650
7651 /***
7652  * DESCRIPTION:
7653  * Creates the listview control.
7654  *
7655  * PARAMETER(S):
7656  * [I] hwnd : window handle
7657  * [I] lpcs : the create parameters
7658  *
7659  * RETURN:
7660  *   Success: 0
7661  *   Failure: -1
7662  */
7663 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7664 {
7665   LISTVIEW_INFO *infoPtr;
7666   UINT uView = lpcs->style & LVS_TYPEMASK;
7667   LOGFONTW logFont;
7668
7669   TRACE("(lpcs=%p)\n", lpcs);
7670
7671   /* initialize info pointer */
7672   infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7673   if (!infoPtr) return -1;
7674
7675   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7676
7677   infoPtr->hwndSelf = hwnd;
7678   infoPtr->dwStyle = lpcs->style;
7679   /* determine the type of structures to use */
7680   infoPtr->hwndNotify = lpcs->hwndParent;
7681   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7682                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7683
7684   /* initialize color information  */
7685   infoPtr->clrBk = CLR_NONE;
7686   infoPtr->clrText = comctl32_color.clrWindowText;
7687   infoPtr->clrTextBk = CLR_DEFAULT;
7688   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7689
7690   /* set default values */
7691   infoPtr->nFocusedItem = -1;
7692   infoPtr->nSelectionMark = -1;
7693   infoPtr->nHotItem = -1;
7694   infoPtr->bRedraw = TRUE;
7695   infoPtr->bNoItemMetrics = TRUE;
7696   infoPtr->bDoChangeNotify = TRUE;
7697   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7698   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7699   infoPtr->nEditLabelItem = -1;
7700   infoPtr->dwHoverTime = -1; /* default system hover time */
7701   infoPtr->nMeasureItemHeight = 0;
7702   infoPtr->xTrackLine = -1;  /* no track line */
7703   infoPtr->itemEdit.fEnabled = FALSE;
7704
7705   /* get default font (icon title) */
7706   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7707   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7708   infoPtr->hFont = infoPtr->hDefaultFont;
7709   LISTVIEW_SaveTextMetrics(infoPtr);
7710
7711   /* create header */
7712   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7713     WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7714     0, 0, 0, 0, hwnd, NULL,
7715     lpcs->hInstance, NULL);
7716   if (!infoPtr->hwndHeader) goto fail;
7717
7718   /* set header unicode format */
7719   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7720
7721   /* set header font */
7722   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7723
7724   /* allocate memory for the data structure */
7725   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7726   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7727   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
7728   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
7729   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7730
7731   /* initialize the icon sizes */
7732   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7733   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7734
7735   /* init item size to avoid division by 0 */
7736   LISTVIEW_UpdateItemSize (infoPtr);
7737   
7738   if (uView == LVS_REPORT)
7739   {
7740     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7741     {
7742       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7743     }
7744     else
7745     {
7746       /* set HDS_HIDDEN flag to hide the header bar */
7747       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7748                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7749     }
7750   }
7751
7752   OpenThemeData(hwnd, themeClass);
7753
7754   return 0;
7755
7756 fail:
7757     DestroyWindow(infoPtr->hwndHeader);
7758     ranges_destroy(infoPtr->selectionRanges);
7759     DPA_Destroy(infoPtr->hdpaItems);
7760     DPA_Destroy(infoPtr->hdpaPosX);
7761     DPA_Destroy(infoPtr->hdpaPosY);
7762     DPA_Destroy(infoPtr->hdpaColumns);
7763     Free(infoPtr);
7764     return -1;
7765 }
7766
7767 /***
7768  * DESCRIPTION:
7769  * Destroys the listview control.
7770  *
7771  * PARAMETER(S):
7772  * [I] infoPtr : valid pointer to the listview structure
7773  *
7774  * RETURN:
7775  *   Success: 0
7776  *   Failure: -1
7777  */
7778 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7779 {
7780     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7781     CloseThemeData(theme);
7782     return 0;
7783 }
7784
7785 /***
7786  * DESCRIPTION:
7787  * Enables the listview control.
7788  *
7789  * PARAMETER(S):
7790  * [I] infoPtr : valid pointer to the listview structure
7791  * [I] bEnable : specifies whether to enable or disable the window
7792  *
7793  * RETURN:
7794  *   SUCCESS : TRUE
7795  *   FAILURE : FALSE
7796  */
7797 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7798 {
7799     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7800         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7801     return TRUE;
7802 }
7803
7804 /***
7805  * DESCRIPTION:
7806  * Erases the background of the listview control.
7807  *
7808  * PARAMETER(S):
7809  * [I] infoPtr : valid pointer to the listview structure
7810  * [I] hdc : device context handle
7811  *
7812  * RETURN:
7813  *   SUCCESS : TRUE
7814  *   FAILURE : FALSE
7815  */
7816 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7817 {
7818     RECT rc;
7819
7820     TRACE("(hdc=%p)\n", hdc);
7821
7822     if (!GetClipBox(hdc, &rc)) return FALSE;
7823
7824     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7825 }
7826         
7827
7828 /***
7829  * DESCRIPTION:
7830  * Helper function for LISTVIEW_[HV]Scroll *only*.
7831  * Performs vertical/horizontal scrolling by a give amount.
7832  *
7833  * PARAMETER(S):
7834  * [I] infoPtr : valid pointer to the listview structure
7835  * [I] dx : amount of horizontal scroll
7836  * [I] dy : amount of vertical scroll
7837  */
7838 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7839 {
7840     /* now we can scroll the list */
7841     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
7842                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7843     /* if we have focus, adjust rect */
7844     OffsetRect(&infoPtr->rcFocus, dx, dy);
7845     UpdateWindow(infoPtr->hwndSelf);
7846 }
7847
7848 /***
7849  * DESCRIPTION:
7850  * Performs vertical scrolling.
7851  *
7852  * PARAMETER(S):
7853  * [I] infoPtr : valid pointer to the listview structure
7854  * [I] nScrollCode : scroll code
7855  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7856  * [I] hScrollWnd  : scrollbar control window handle
7857  *
7858  * RETURN:
7859  * Zero
7860  *
7861  * NOTES:
7862  *   SB_LINEUP/SB_LINEDOWN:
7863  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
7864  *        for LVS_REPORT is 1 line
7865  *        for LVS_LIST cannot occur
7866  *
7867  */
7868 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
7869                                 INT nScrollDiff, HWND hScrollWnd)
7870 {
7871     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7872     INT nOldScrollPos, nNewScrollPos;
7873     SCROLLINFO scrollInfo;
7874     BOOL is_an_icon;
7875
7876     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7877         debugscrollcode(nScrollCode), nScrollDiff);
7878
7879     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7880
7881     scrollInfo.cbSize = sizeof(SCROLLINFO);
7882     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7883
7884     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7885
7886     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7887
7888     nOldScrollPos = scrollInfo.nPos;
7889     switch (nScrollCode)
7890     {
7891     case SB_INTERNAL:
7892         break;
7893
7894     case SB_LINEUP:
7895         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7896         break;
7897
7898     case SB_LINEDOWN:
7899         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7900         break;
7901
7902     case SB_PAGEUP:
7903         nScrollDiff = -scrollInfo.nPage;
7904         break;
7905
7906     case SB_PAGEDOWN:
7907         nScrollDiff = scrollInfo.nPage;
7908         break;
7909
7910     case SB_THUMBPOSITION:
7911     case SB_THUMBTRACK:
7912         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7913         break;
7914
7915     default:
7916         nScrollDiff = 0;
7917     }
7918
7919     /* quit right away if pos isn't changing */
7920     if (nScrollDiff == 0) return 0;
7921     
7922     /* calculate new position, and handle overflows */
7923     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7924     if (nScrollDiff > 0) {
7925         if (nNewScrollPos < nOldScrollPos ||
7926             nNewScrollPos > scrollInfo.nMax)
7927             nNewScrollPos = scrollInfo.nMax;
7928     } else {
7929         if (nNewScrollPos > nOldScrollPos ||
7930             nNewScrollPos < scrollInfo.nMin)
7931             nNewScrollPos = scrollInfo.nMin;
7932     }
7933
7934     /* set the new position, and reread in case it changed */
7935     scrollInfo.fMask = SIF_POS;
7936     scrollInfo.nPos = nNewScrollPos;
7937     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7938     
7939     /* carry on only if it really changed */
7940     if (nNewScrollPos == nOldScrollPos) return 0;
7941     
7942     /* now adjust to client coordinates */
7943     nScrollDiff = nOldScrollPos - nNewScrollPos;
7944     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7945    
7946     /* and scroll the window */ 
7947     scroll_list(infoPtr, 0, nScrollDiff);
7948
7949     return 0;
7950 }
7951
7952 /***
7953  * DESCRIPTION:
7954  * Performs horizontal scrolling.
7955  *
7956  * PARAMETER(S):
7957  * [I] infoPtr : valid pointer to the listview structure
7958  * [I] nScrollCode : scroll code
7959  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7960  * [I] hScrollWnd  : scrollbar control window handle
7961  *
7962  * RETURN:
7963  * Zero
7964  *
7965  * NOTES:
7966  *   SB_LINELEFT/SB_LINERIGHT:
7967  *        for LVS_ICON, LVS_SMALLICON  1 pixel
7968  *        for LVS_REPORT is 1 pixel
7969  *        for LVS_LIST  is 1 column --> which is a 1 because the
7970  *                                      scroll is based on columns not pixels
7971  *
7972  */
7973 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7974                                 INT nScrollDiff, HWND hScrollWnd)
7975 {
7976     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7977     INT nOldScrollPos, nNewScrollPos;
7978     SCROLLINFO scrollInfo;
7979
7980     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7981         debugscrollcode(nScrollCode), nScrollDiff);
7982
7983     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7984
7985     scrollInfo.cbSize = sizeof(SCROLLINFO);
7986     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7987
7988     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7989
7990     nOldScrollPos = scrollInfo.nPos;
7991
7992     switch (nScrollCode)
7993     {
7994     case SB_INTERNAL:
7995         break;
7996
7997     case SB_LINELEFT:
7998         nScrollDiff = -1;
7999         break;
8000
8001     case SB_LINERIGHT:
8002         nScrollDiff = 1;
8003         break;
8004
8005     case SB_PAGELEFT:
8006         nScrollDiff = -scrollInfo.nPage;
8007         break;
8008
8009     case SB_PAGERIGHT:
8010         nScrollDiff = scrollInfo.nPage;
8011         break;
8012
8013     case SB_THUMBPOSITION:
8014     case SB_THUMBTRACK:
8015         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8016         break;
8017
8018     default:
8019         nScrollDiff = 0;
8020     }
8021
8022     /* quit right away if pos isn't changing */
8023     if (nScrollDiff == 0) return 0;
8024     
8025     /* calculate new position, and handle overflows */
8026     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8027     if (nScrollDiff > 0) {
8028         if (nNewScrollPos < nOldScrollPos ||
8029             nNewScrollPos > scrollInfo.nMax)
8030             nNewScrollPos = scrollInfo.nMax;
8031     } else {
8032         if (nNewScrollPos > nOldScrollPos ||
8033             nNewScrollPos < scrollInfo.nMin)
8034             nNewScrollPos = scrollInfo.nMin;
8035     }
8036
8037     /* set the new position, and reread in case it changed */
8038     scrollInfo.fMask = SIF_POS;
8039     scrollInfo.nPos = nNewScrollPos;
8040     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8041     
8042     /* carry on only if it really changed */
8043     if (nNewScrollPos == nOldScrollPos) return 0;
8044     
8045     if(uView == LVS_REPORT)
8046         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8047       
8048     /* now adjust to client coordinates */
8049     nScrollDiff = nOldScrollPos - nNewScrollPos;
8050     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8051    
8052     /* and scroll the window */
8053     scroll_list(infoPtr, nScrollDiff, 0);
8054
8055   return 0;
8056 }
8057
8058 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8059 {
8060     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8061     INT gcWheelDelta = 0;
8062     INT pulScrollLines = 3;
8063     SCROLLINFO scrollInfo;
8064
8065     TRACE("(wheelDelta=%d)\n", wheelDelta);
8066
8067     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8068     gcWheelDelta -= wheelDelta;
8069
8070     scrollInfo.cbSize = sizeof(SCROLLINFO);
8071     scrollInfo.fMask = SIF_POS;
8072
8073     switch(uView)
8074     {
8075     case LVS_ICON:
8076     case LVS_SMALLICON:
8077        /*
8078         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8079         *  should be fixed in the future.
8080         */
8081         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8082                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8083         break;
8084
8085     case LVS_REPORT:
8086         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8087         {
8088             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8089             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8090             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8091         }
8092         break;
8093
8094     case LVS_LIST:
8095         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8096         break;
8097     }
8098     return 0;
8099 }
8100
8101 /***
8102  * DESCRIPTION:
8103  * ???
8104  *
8105  * PARAMETER(S):
8106  * [I] infoPtr : valid pointer to the listview structure
8107  * [I] nVirtualKey : virtual key
8108  * [I] lKeyData : key data
8109  *
8110  * RETURN:
8111  * Zero
8112  */
8113 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8114 {
8115   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8116   HWND hwndSelf = infoPtr->hwndSelf;
8117   INT nItem = -1;
8118   NMLVKEYDOWN nmKeyDown;
8119
8120   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8121
8122   /* send LVN_KEYDOWN notification */
8123   nmKeyDown.wVKey = nVirtualKey;
8124   nmKeyDown.flags = 0;
8125   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8126   if (!IsWindow(hwndSelf))
8127     return 0;
8128
8129   switch (nVirtualKey)
8130   {
8131   case VK_SPACE:
8132     nItem = infoPtr->nFocusedItem;
8133     break;
8134
8135   case VK_RETURN:
8136     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8137     {
8138         if (!notify(infoPtr, NM_RETURN)) return 0;
8139         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8140     }
8141     break;
8142
8143   case VK_HOME:
8144     if (infoPtr->nItemCount > 0)
8145       nItem = 0;
8146     break;
8147
8148   case VK_END:
8149     if (infoPtr->nItemCount > 0)
8150       nItem = infoPtr->nItemCount - 1;
8151     break;
8152
8153   case VK_LEFT:
8154     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8155     break;
8156
8157   case VK_UP:
8158     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8159     break;
8160
8161   case VK_RIGHT:
8162     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8163     break;
8164
8165   case VK_DOWN:
8166     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8167     break;
8168
8169   case VK_PRIOR:
8170     if (uView == LVS_REPORT)
8171     {
8172       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8173       if (infoPtr->nFocusedItem == topidx)
8174         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8175       else
8176         nItem = topidx;
8177     }
8178     else
8179       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8180                                     * LISTVIEW_GetCountPerRow(infoPtr);
8181     if(nItem < 0) nItem = 0;
8182     break;
8183
8184   case VK_NEXT:
8185     if (uView == LVS_REPORT)
8186     {
8187       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8188       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8189       if (infoPtr->nFocusedItem == topidx + cnt - 1)
8190         nItem = infoPtr->nFocusedItem + cnt - 1;
8191       else
8192         nItem = topidx + cnt - 1;
8193     }
8194     else
8195       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8196                                     * LISTVIEW_GetCountPerRow(infoPtr);
8197     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8198     break;
8199   }
8200
8201   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8202       LISTVIEW_KeySelection(infoPtr, nItem);
8203
8204   return 0;
8205 }
8206
8207 /***
8208  * DESCRIPTION:
8209  * Kills the focus.
8210  *
8211  * PARAMETER(S):
8212  * [I] infoPtr : valid pointer to the listview structure
8213  *
8214  * RETURN:
8215  * Zero
8216  */
8217 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8218 {
8219     TRACE("()\n");
8220
8221     /* if we did not have the focus, there's nothing to do */
8222     if (!infoPtr->bFocus) return 0;
8223    
8224     /* send NM_KILLFOCUS notification */
8225     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8226
8227     /* if we have a focus rectagle, get rid of it */
8228     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8229     
8230     /* set window focus flag */
8231     infoPtr->bFocus = FALSE;
8232
8233     /* invalidate the selected items before reseting focus flag */
8234     LISTVIEW_InvalidateSelectedItems(infoPtr);
8235     
8236     return 0;
8237 }
8238
8239 /***
8240  * DESCRIPTION:
8241  * Processes double click messages (left mouse button).
8242  *
8243  * PARAMETER(S):
8244  * [I] infoPtr : valid pointer to the listview structure
8245  * [I] wKey : key flag
8246  * [I] x,y : mouse coordinate
8247  *
8248  * RETURN:
8249  * Zero
8250  */
8251 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8252 {
8253     LVHITTESTINFO htInfo;
8254
8255     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8256     
8257     /* Cancel the item edition if any */
8258     if (infoPtr->itemEdit.fEnabled)
8259     {
8260       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8261       infoPtr->itemEdit.fEnabled = FALSE;
8262     }
8263
8264     /* send NM_RELEASEDCAPTURE notification */
8265     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8266
8267     htInfo.pt.x = x;
8268     htInfo.pt.y = y;
8269
8270     /* send NM_DBLCLK notification */
8271     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8272     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8273
8274     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8275     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8276
8277     return 0;
8278 }
8279
8280 /***
8281  * DESCRIPTION:
8282  * Processes mouse down messages (left mouse button).
8283  *
8284  * PARAMETERS:
8285  *   infoPtr  [I ] valid pointer to the listview structure
8286  *   wKey     [I ] key flag
8287  *   x,y      [I ] mouse coordinate
8288  *
8289  * RETURN:
8290  *   Zero
8291  */
8292 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8293 {
8294   LVHITTESTINFO lvHitTestInfo;
8295   static BOOL bGroupSelect = TRUE;
8296   BOOL bReceivedFocus = FALSE;
8297   POINT pt = { x, y };
8298   INT nItem;
8299
8300   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8301
8302   /* send NM_RELEASEDCAPTURE notification */
8303   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8304
8305   if (!infoPtr->bFocus)
8306   {
8307     bReceivedFocus = TRUE;
8308     SetFocus(infoPtr->hwndSelf);
8309   }
8310
8311   /* set left button down flag and record the click position */
8312   infoPtr->bLButtonDown = TRUE;
8313   infoPtr->ptClickPos = pt;
8314
8315   lvHitTestInfo.pt.x = x;
8316   lvHitTestInfo.pt.y = y;
8317
8318   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8319   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8320   infoPtr->nEditLabelItem = -1;
8321   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8322   {
8323     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8324     {
8325         DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8326         if(state == 1 || state == 2)
8327         {
8328             LVITEMW lvitem;
8329             state ^= 3;
8330             lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8331             lvitem.stateMask = LVIS_STATEIMAGEMASK;
8332             LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8333         }
8334         return 0;
8335     }
8336
8337     if (infoPtr->dwStyle & LVS_SINGLESEL)
8338     {
8339       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8340         infoPtr->nEditLabelItem = nItem;
8341       else
8342         LISTVIEW_SetSelection(infoPtr, nItem);
8343     }
8344     else
8345     {
8346       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8347       {
8348         if (bGroupSelect)
8349         {
8350           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8351           LISTVIEW_SetItemFocus(infoPtr, nItem);
8352           infoPtr->nSelectionMark = nItem;
8353         }
8354         else
8355         {
8356           LVITEMW item;
8357
8358           item.state = LVIS_SELECTED | LVIS_FOCUSED;
8359           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8360
8361           LISTVIEW_SetItemState(infoPtr,nItem,&item);
8362           infoPtr->nSelectionMark = nItem;
8363         }
8364       }
8365       else if (wKey & MK_CONTROL)
8366       {
8367         LVITEMW item;
8368
8369         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8370         
8371         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8372         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8373         LISTVIEW_SetItemState(infoPtr, nItem, &item);
8374         infoPtr->nSelectionMark = nItem;
8375       }
8376       else  if (wKey & MK_SHIFT)
8377       {
8378         LISTVIEW_SetGroupSelection(infoPtr, nItem);
8379       }
8380       else
8381       {
8382         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8383           infoPtr->nEditLabelItem = nItem;
8384
8385         /* set selection (clears other pre-existing selections) */
8386         LISTVIEW_SetSelection(infoPtr, nItem);
8387       }
8388     }
8389   }
8390   else
8391   {
8392     /* remove all selections */
8393     LISTVIEW_DeselectAll(infoPtr);
8394     ReleaseCapture();
8395   }
8396   
8397   if (bReceivedFocus)
8398     infoPtr->nEditLabelItem = -1;
8399
8400   return 0;
8401 }
8402
8403 /***
8404  * DESCRIPTION:
8405  * Processes mouse up messages (left mouse button).
8406  *
8407  * PARAMETERS:
8408  *   infoPtr [I ] valid pointer to the listview structure
8409  *   wKey    [I ] key flag
8410  *   x,y     [I ] mouse coordinate
8411  *
8412  * RETURN:
8413  *   Zero
8414  */
8415 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8416 {
8417     LVHITTESTINFO lvHitTestInfo;
8418     
8419     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8420
8421     if (!infoPtr->bLButtonDown) return 0;
8422
8423     lvHitTestInfo.pt.x = x;
8424     lvHitTestInfo.pt.y = y;
8425
8426     /* send NM_CLICK notification */
8427     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8428     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8429
8430     /* set left button flag */
8431     infoPtr->bLButtonDown = FALSE;
8432
8433     /* if we clicked on a selected item, edit the label */
8434     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8435     {
8436         /* we want to make sure the user doesn't want to do a double click. So we will
8437          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8438          */
8439         infoPtr->itemEdit.fEnabled = TRUE;
8440         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8441         SetTimer(infoPtr->hwndSelf,
8442             (UINT_PTR)&infoPtr->itemEdit,
8443             GetDoubleClickTime(),
8444             LISTVIEW_DelayedEditItem);
8445     }
8446
8447     return 0;
8448 }
8449
8450 /***
8451  * DESCRIPTION:
8452  * Destroys the listview control (called after WM_DESTROY).
8453  *
8454  * PARAMETER(S):
8455  * [I] infoPtr : valid pointer to the listview structure
8456  *
8457  * RETURN:
8458  * Zero
8459  */
8460 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8461 {
8462   TRACE("()\n");
8463
8464   /* delete all items */
8465   LISTVIEW_DeleteAllItems(infoPtr);
8466
8467   /* destroy data structure */
8468   DPA_Destroy(infoPtr->hdpaItems);
8469   DPA_Destroy(infoPtr->hdpaPosX);
8470   DPA_Destroy(infoPtr->hdpaPosY);
8471   DPA_Destroy(infoPtr->hdpaColumns);
8472   ranges_destroy(infoPtr->selectionRanges);
8473
8474   /* destroy image lists */
8475   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8476   {
8477       if (infoPtr->himlNormal)
8478           ImageList_Destroy(infoPtr->himlNormal);
8479       if (infoPtr->himlSmall)
8480           ImageList_Destroy(infoPtr->himlSmall);
8481       if (infoPtr->himlState)
8482           ImageList_Destroy(infoPtr->himlState);
8483   }
8484
8485   /* destroy font, bkgnd brush */
8486   infoPtr->hFont = 0;
8487   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8488   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8489
8490   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8491
8492   /* free listview info pointer*/
8493   Free(infoPtr);
8494
8495   return 0;
8496 }
8497
8498 /***
8499  * DESCRIPTION:
8500  * Handles notifications from header.
8501  *
8502  * PARAMETER(S):
8503  * [I] infoPtr : valid pointer to the listview structure
8504  * [I] nCtrlId : control identifier
8505  * [I] lpnmh : notification information
8506  *
8507  * RETURN:
8508  * Zero
8509  */
8510 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8511 {
8512     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8513     HWND hwndSelf = infoPtr->hwndSelf;
8514     
8515     TRACE("(lpnmh=%p)\n", lpnmh);
8516
8517     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8518     
8519     switch (lpnmh->hdr.code)
8520     {    
8521         case HDN_TRACKW:
8522         case HDN_TRACKA:
8523         {
8524             COLUMN_INFO *lpColumnInfo;
8525             POINT ptOrigin;
8526             INT x;
8527             
8528             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8529                 break;
8530
8531             /* remove the old line (if any) */
8532             LISTVIEW_DrawTrackLine(infoPtr);
8533             
8534             /* compute & draw the new line */
8535             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8536             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8537             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8538             infoPtr->xTrackLine = x + ptOrigin.x;
8539             LISTVIEW_DrawTrackLine(infoPtr);
8540             break;
8541         }
8542         
8543         case HDN_ENDTRACKA:
8544         case HDN_ENDTRACKW:
8545             /* remove the track line (if any) */
8546             LISTVIEW_DrawTrackLine(infoPtr);
8547             infoPtr->xTrackLine = -1;
8548             break;
8549             
8550         case HDN_ENDDRAG:
8551             FIXME("Changing column order not implemented\n");
8552             return TRUE;
8553             
8554         case HDN_ITEMCHANGINGW:
8555         case HDN_ITEMCHANGINGA:
8556             return notify_forward_header(infoPtr, lpnmh);
8557             
8558         case HDN_ITEMCHANGEDW:
8559         case HDN_ITEMCHANGEDA:
8560         {
8561             COLUMN_INFO *lpColumnInfo;
8562             INT dx, cxy;
8563             
8564             notify_forward_header(infoPtr, lpnmh);
8565             if (!IsWindow(hwndSelf))
8566                 break;
8567
8568             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8569             {
8570                 HDITEMW hdi;
8571     
8572                 hdi.mask = HDI_WIDTH;
8573                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8574                 cxy = hdi.cxy;
8575             }
8576             else
8577                 cxy = lpnmh->pitem->cxy;
8578             
8579             /* determine how much we change since the last know position */
8580             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8581             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8582             if (dx != 0)
8583             {
8584                 lpColumnInfo->rcHeader.right += dx;
8585                 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8586                     LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8587                 else
8588                 {
8589                     /* only needs to update the scrolls */
8590                     infoPtr->nItemWidth += dx;
8591                     LISTVIEW_UpdateScroll(infoPtr);
8592                 }
8593                 LISTVIEW_UpdateItemSize(infoPtr);
8594                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8595                 {
8596                     POINT ptOrigin;
8597                     RECT rcCol = lpColumnInfo->rcHeader;
8598                     
8599                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8600                     OffsetRect(&rcCol, ptOrigin.x, 0);
8601                     
8602                     rcCol.top = infoPtr->rcList.top;
8603                     rcCol.bottom = infoPtr->rcList.bottom;
8604
8605                     /* resizing left-aligned columns leaves most of the left side untouched */
8606                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8607                     {
8608                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8609                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8610                     }
8611                     
8612                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8613                 }
8614             }
8615         }
8616         break;
8617
8618         case HDN_ITEMCLICKW:
8619         case HDN_ITEMCLICKA:
8620         {
8621             /* Handle sorting by Header Column */
8622             NMLISTVIEW nmlv;
8623
8624             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8625             nmlv.iItem = -1;
8626             nmlv.iSubItem = lpnmh->iItem;
8627             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8628         }
8629         break;
8630
8631         case HDN_DIVIDERDBLCLICKW:
8632         case HDN_DIVIDERDBLCLICKA:
8633             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8634             break;
8635     }
8636
8637     return 0;
8638 }
8639
8640 /***
8641  * DESCRIPTION:
8642  * Paint non-client area of control.
8643  *
8644  * PARAMETER(S):
8645  * [I] infoPtr : valid pointer to the listview structureof the sender
8646  * [I] region : update region
8647  *
8648  * RETURN:
8649  *  TRUE  - frame was painted
8650  *  FALSE - call default window proc
8651  */
8652 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8653 {
8654     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8655     HDC dc;
8656     RECT r;
8657     HRGN cliprgn;
8658     int cxEdge = GetSystemMetrics (SM_CXEDGE),
8659         cyEdge = GetSystemMetrics (SM_CYEDGE);
8660
8661     if (!theme) return FALSE;
8662
8663     GetWindowRect(infoPtr->hwndSelf, &r);
8664
8665     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8666         r.right - cxEdge, r.bottom - cyEdge);
8667     if (region != (HRGN)1)
8668         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8669     OffsetRect(&r, -r.left, -r.top);
8670
8671     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8672     OffsetRect(&r, -r.left, -r.top);
8673
8674     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8675         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8676     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8677     ReleaseDC(infoPtr->hwndSelf, dc);
8678
8679     /* Call default proc to get the scrollbars etc. painted */
8680     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8681
8682     return TRUE;
8683 }
8684
8685 /***
8686  * DESCRIPTION:
8687  * Determines the type of structure to use.
8688  *
8689  * PARAMETER(S):
8690  * [I] infoPtr : valid pointer to the listview structureof the sender
8691  * [I] hwndFrom : listview window handle
8692  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8693  *
8694  * RETURN:
8695  * Zero
8696  */
8697 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8698 {
8699     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8700
8701     if (nCommand != NF_REQUERY) return 0;
8702     
8703     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8704     
8705     return 0;
8706 }
8707
8708 /***
8709  * DESCRIPTION:
8710  * Paints/Repaints the listview control.
8711  *
8712  * PARAMETER(S):
8713  * [I] infoPtr : valid pointer to the listview structure
8714  * [I] hdc : device context handle
8715  *
8716  * RETURN:
8717  * Zero
8718  */
8719 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8720 {
8721     TRACE("(hdc=%p)\n", hdc);
8722
8723     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8724     {
8725         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8726         
8727         infoPtr->bNoItemMetrics = FALSE;
8728         LISTVIEW_UpdateItemSize(infoPtr);
8729         if (uView == LVS_ICON || uView == LVS_SMALLICON)
8730             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8731         LISTVIEW_UpdateScroll(infoPtr);
8732     }
8733     if (hdc) 
8734         LISTVIEW_Refresh(infoPtr, hdc);
8735     else
8736     {
8737         PAINTSTRUCT ps;
8738
8739         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8740         if (!hdc) return 1;
8741         if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8742         LISTVIEW_Refresh(infoPtr, hdc);
8743         EndPaint(infoPtr->hwndSelf, &ps);
8744     }
8745
8746     return 0;
8747 }
8748
8749
8750 /***
8751  * DESCRIPTION:
8752  * Paints/Repaints the listview control.
8753  *
8754  * PARAMETER(S):
8755  * [I] infoPtr : valid pointer to the listview structure
8756  * [I] hdc : device context handle
8757  * [I] options : drawing options
8758  *
8759  * RETURN:
8760  * Zero
8761  */
8762 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8763 {
8764     FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
8765
8766     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8767         return 0;
8768
8769     if (options & PRF_ERASEBKGND)
8770         LISTVIEW_EraseBkgnd(infoPtr, hdc);
8771
8772     if (options & PRF_CLIENT)
8773         LISTVIEW_Paint(infoPtr, hdc);
8774
8775     return 0;
8776 }
8777
8778
8779 /***
8780  * DESCRIPTION:
8781  * Processes double click messages (right mouse button).
8782  *
8783  * PARAMETER(S):
8784  * [I] infoPtr : valid pointer to the listview structure
8785  * [I] wKey : key flag
8786  * [I] x,y : mouse coordinate
8787  *
8788  * RETURN:
8789  * Zero
8790  */
8791 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8792 {
8793     LVHITTESTINFO lvHitTestInfo;
8794     
8795     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8796
8797     /* send NM_RELEASEDCAPTURE notification */
8798     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8799
8800     /* send NM_RDBLCLK notification */
8801     lvHitTestInfo.pt.x = x;
8802     lvHitTestInfo.pt.y = y;
8803     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8804     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8805
8806     return 0;
8807 }
8808
8809 /***
8810  * DESCRIPTION:
8811  * Processes mouse down messages (right mouse button).
8812  *
8813  * PARAMETER(S):
8814  * [I] infoPtr : valid pointer to the listview structure
8815  * [I] wKey : key flag
8816  * [I] x,y : mouse coordinate
8817  *
8818  * RETURN:
8819  * Zero
8820  */
8821 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8822 {
8823     LVHITTESTINFO lvHitTestInfo;
8824     INT nItem;
8825
8826     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8827
8828     /* send NM_RELEASEDCAPTURE notification */
8829     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8830
8831     /* make sure the listview control window has the focus */
8832     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8833
8834     /* set right button down flag */
8835     infoPtr->bRButtonDown = TRUE;
8836
8837     /* determine the index of the selected item */
8838     lvHitTestInfo.pt.x = x;
8839     lvHitTestInfo.pt.y = y;
8840     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8841   
8842     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8843     {
8844         LISTVIEW_SetItemFocus(infoPtr, nItem);
8845         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8846             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8847             LISTVIEW_SetSelection(infoPtr, nItem);
8848     }
8849     else
8850     {
8851         LISTVIEW_DeselectAll(infoPtr);
8852     }
8853
8854     return 0;
8855 }
8856
8857 /***
8858  * DESCRIPTION:
8859  * Processes mouse up messages (right mouse button).
8860  *
8861  * PARAMETER(S):
8862  * [I] infoPtr : valid pointer to the listview structure
8863  * [I] wKey : key flag
8864  * [I] x,y : mouse coordinate
8865  *
8866  * RETURN:
8867  * Zero
8868  */
8869 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8870 {
8871     LVHITTESTINFO lvHitTestInfo;
8872     POINT pt;
8873
8874     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8875
8876     if (!infoPtr->bRButtonDown) return 0;
8877  
8878     /* set button flag */
8879     infoPtr->bRButtonDown = FALSE;
8880
8881     /* Send NM_RClICK notification */
8882     lvHitTestInfo.pt.x = x;
8883     lvHitTestInfo.pt.y = y;
8884     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8885     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8886
8887     /* Change to screen coordinate for WM_CONTEXTMENU */
8888     pt = lvHitTestInfo.pt;
8889     ClientToScreen(infoPtr->hwndSelf, &pt);
8890
8891     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8892     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8893                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8894
8895     return 0;
8896 }
8897
8898
8899 /***
8900  * DESCRIPTION:
8901  * Sets the cursor.
8902  *
8903  * PARAMETER(S):
8904  * [I] infoPtr : valid pointer to the listview structure
8905  * [I] hwnd : window handle of window containing the cursor
8906  * [I] nHittest : hit-test code
8907  * [I] wMouseMsg : ideintifier of the mouse message
8908  *
8909  * RETURN:
8910  * TRUE if cursor is set
8911  * FALSE otherwise
8912  */
8913 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8914 {
8915     LVHITTESTINFO lvHitTestInfo;
8916
8917     if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8918
8919     if(!infoPtr->hHotCursor)  return FALSE;
8920
8921     GetCursorPos(&lvHitTestInfo.pt);
8922     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8923
8924     SetCursor(infoPtr->hHotCursor);
8925
8926     return TRUE;
8927 }
8928
8929 /***
8930  * DESCRIPTION:
8931  * Sets the focus.
8932  *
8933  * PARAMETER(S):
8934  * [I] infoPtr : valid pointer to the listview structure
8935  * [I] hwndLoseFocus : handle of previously focused window
8936  *
8937  * RETURN:
8938  * Zero
8939  */
8940 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8941 {
8942     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8943
8944     /* if we have the focus already, there's nothing to do */
8945     if (infoPtr->bFocus) return 0;
8946    
8947     /* send NM_SETFOCUS notification */
8948     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8949
8950     /* set window focus flag */
8951     infoPtr->bFocus = TRUE;
8952
8953     /* put the focus rect back on */
8954     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8955
8956     /* redraw all visible selected items */
8957     LISTVIEW_InvalidateSelectedItems(infoPtr);
8958
8959     return 0;
8960 }
8961
8962 /***
8963  * DESCRIPTION:
8964  * Sets the font.
8965  *
8966  * PARAMETER(S):
8967  * [I] infoPtr : valid pointer to the listview structure
8968  * [I] fRedraw : font handle
8969  * [I] fRedraw : redraw flag
8970  *
8971  * RETURN:
8972  * Zero
8973  */
8974 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8975 {
8976     HFONT oldFont = infoPtr->hFont;
8977
8978     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8979
8980     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8981     if (infoPtr->hFont == oldFont) return 0;
8982     
8983     LISTVIEW_SaveTextMetrics(infoPtr);
8984
8985     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8986         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8987
8988     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8989
8990     return 0;
8991 }
8992
8993 /***
8994  * DESCRIPTION:
8995  * Message handling for WM_SETREDRAW.
8996  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8997  *
8998  * PARAMETER(S):
8999  * [I] infoPtr : valid pointer to the listview structure
9000  * [I] bRedraw: state of redraw flag
9001  *
9002  * RETURN:
9003  * DefWinProc return value
9004  */
9005 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9006 {
9007     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9008
9009     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9010     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9011
9012     infoPtr->bRedraw = bRedraw;
9013
9014     if(!bRedraw) return 0;
9015     
9016     if (is_autoarrange(infoPtr))
9017         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9018     LISTVIEW_UpdateScroll(infoPtr);
9019
9020     /* despite what the WM_SETREDRAW docs says, apps expect us
9021      * to invalidate the listview here... stupid! */
9022     LISTVIEW_InvalidateList(infoPtr);
9023
9024     return 0;
9025 }
9026
9027 /***
9028  * DESCRIPTION:
9029  * Resizes the listview control. This function processes WM_SIZE
9030  * messages.  At this time, the width and height are not used.
9031  *
9032  * PARAMETER(S):
9033  * [I] infoPtr : valid pointer to the listview structure
9034  * [I] Width : new width
9035  * [I] Height : new height
9036  *
9037  * RETURN:
9038  * Zero
9039  */
9040 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9041 {
9042     RECT rcOld = infoPtr->rcList;
9043
9044     TRACE("(width=%d, height=%d)\n", Width, Height);
9045
9046     LISTVIEW_UpdateSize(infoPtr);
9047     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9048   
9049     /* do not bother with display related stuff if we're not redrawing */ 
9050     if (!is_redrawing(infoPtr)) return 0;
9051     
9052     if (is_autoarrange(infoPtr)) 
9053         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9054
9055     LISTVIEW_UpdateScroll(infoPtr);
9056
9057     /* refresh all only for lists whose height changed significantly */
9058     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
9059         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9060         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9061         LISTVIEW_InvalidateList(infoPtr);
9062
9063   return 0;
9064 }
9065
9066 /***
9067  * DESCRIPTION:
9068  * Sets the size information.
9069  *
9070  * PARAMETER(S):
9071  * [I] infoPtr : valid pointer to the listview structure
9072  *
9073  * RETURN:
9074  *  None
9075  */
9076 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9077 {
9078     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9079
9080     TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9081     
9082     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9083
9084     if (uView == LVS_LIST)
9085     {
9086         /* Apparently the "LIST" style is supposed to have the same
9087          * number of items in a column even if there is no scroll bar.
9088          * Since if a scroll bar already exists then the bottom is already
9089          * reduced, only reduce if the scroll bar does not currently exist.
9090          * The "2" is there to mimic the native control. I think it may be
9091          * related to either padding or edges.  (GLA 7/2002)
9092          */
9093         if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9094             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9095         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9096     }
9097     else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9098     {
9099         HDLAYOUT hl;
9100         WINDOWPOS wp;
9101
9102         hl.prc = &infoPtr->rcList;
9103         hl.pwpos = &wp;
9104         SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9105
9106         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9107
9108         infoPtr->rcList.top = max(wp.cy, 0);
9109     }
9110
9111     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9112 }
9113
9114 /***
9115  * DESCRIPTION:
9116  * Processes WM_STYLECHANGED messages.
9117  *
9118  * PARAMETER(S):
9119  * [I] infoPtr : valid pointer to the listview structure
9120  * [I] wStyleType : window style type (normal or extended)
9121  * [I] lpss : window style information
9122  *
9123  * RETURN:
9124  * Zero
9125  */
9126 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9127                                  const STYLESTRUCT *lpss)
9128 {
9129     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9130     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9131
9132     TRACE("(styletype=%x, styleOld=0x%08x, styleNew=0x%08x)\n",
9133           wStyleType, lpss->styleOld, lpss->styleNew);
9134
9135     if (wStyleType != GWL_STYLE) return 0;
9136   
9137     /* FIXME: if LVS_NOSORTHEADER changed, update header */
9138     /*        what if LVS_OWNERDATA changed? */
9139     /*        or LVS_SINGLESEL */
9140     /*        or LVS_SORT{AS,DES}CENDING */
9141
9142     infoPtr->dwStyle = lpss->styleNew;
9143
9144     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9145         ((lpss->styleNew & WS_HSCROLL) == 0))
9146        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9147
9148     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9149         ((lpss->styleNew & WS_VSCROLL) == 0))
9150        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9151
9152     if (uNewView != uOldView)
9153     {
9154         SIZE oldIconSize = infoPtr->iconSize;
9155         HIMAGELIST himl;
9156     
9157         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9158         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9159
9160         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9161         SetRectEmpty(&infoPtr->rcFocus);
9162
9163         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9164         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9165     
9166         if (uNewView == LVS_ICON)
9167         {
9168             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9169             {
9170                 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9171                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9172                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9173             }
9174         }
9175         else if (uNewView == LVS_REPORT)
9176         {
9177             HDLAYOUT hl;
9178             WINDOWPOS wp;
9179
9180             hl.prc = &infoPtr->rcList;
9181             hl.pwpos = &wp;
9182             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9183             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9184         }
9185
9186         LISTVIEW_UpdateItemSize(infoPtr);
9187     }
9188
9189     if (uNewView == LVS_REPORT)
9190         ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9191      
9192     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9193          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9194          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9195
9196     /* update the size of the client area */
9197     LISTVIEW_UpdateSize(infoPtr);
9198
9199     /* add scrollbars if needed */
9200     LISTVIEW_UpdateScroll(infoPtr);
9201
9202     /* invalidate client area + erase background */
9203     LISTVIEW_InvalidateList(infoPtr);
9204
9205     return 0;
9206 }
9207
9208 /***
9209  * DESCRIPTION:
9210  * Window procedure of the listview control.
9211  *
9212  */
9213 static LRESULT WINAPI
9214 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9215 {
9216   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9217
9218   TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9219
9220   if (!infoPtr && (uMsg != WM_CREATE))
9221     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9222
9223   switch (uMsg)
9224   {
9225   case LVM_APPROXIMATEVIEWRECT:
9226     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9227                                         LOWORD(lParam), HIWORD(lParam));
9228   case LVM_ARRANGE:
9229     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9230
9231 /* case LVM_CANCELEDITLABEL: */
9232
9233   case LVM_CREATEDRAGIMAGE:
9234     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9235
9236   case LVM_DELETEALLITEMS:
9237     return LISTVIEW_DeleteAllItems(infoPtr);
9238
9239   case LVM_DELETECOLUMN:
9240     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9241
9242   case LVM_DELETEITEM:
9243     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9244
9245   case LVM_EDITLABELW:
9246     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9247
9248   case LVM_EDITLABELA:
9249     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9250
9251   /* case LVM_ENABLEGROUPVIEW: */
9252
9253   case LVM_ENSUREVISIBLE:
9254     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9255
9256   case LVM_FINDITEMW:
9257     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9258
9259   case LVM_FINDITEMA:
9260     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9261
9262   case LVM_GETBKCOLOR:
9263     return infoPtr->clrBk;
9264
9265   /* case LVM_GETBKIMAGE: */
9266
9267   case LVM_GETCALLBACKMASK:
9268     return infoPtr->uCallbackMask;
9269
9270   case LVM_GETCOLUMNA:
9271     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9272
9273   case LVM_GETCOLUMNW:
9274     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9275
9276   case LVM_GETCOLUMNORDERARRAY:
9277     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9278
9279   case LVM_GETCOLUMNWIDTH:
9280     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9281
9282   case LVM_GETCOUNTPERPAGE:
9283     return LISTVIEW_GetCountPerPage(infoPtr);
9284
9285   case LVM_GETEDITCONTROL:
9286     return (LRESULT)infoPtr->hwndEdit;
9287
9288   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9289     return infoPtr->dwLvExStyle;
9290
9291   /* case LVM_GETGROUPINFO: */
9292
9293   /* case LVM_GETGROUPMETRICS: */
9294
9295   case LVM_GETHEADER:
9296     return (LRESULT)infoPtr->hwndHeader;
9297
9298   case LVM_GETHOTCURSOR:
9299     return (LRESULT)infoPtr->hHotCursor;
9300
9301   case LVM_GETHOTITEM:
9302     return infoPtr->nHotItem;
9303
9304   case LVM_GETHOVERTIME:
9305     return infoPtr->dwHoverTime;
9306
9307   case LVM_GETIMAGELIST:
9308     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9309
9310   /* case LVM_GETINSERTMARK: */
9311
9312   /* case LVM_GETINSERTMARKCOLOR: */
9313
9314   /* case LVM_GETINSERTMARKRECT: */
9315
9316   case LVM_GETISEARCHSTRINGA:
9317   case LVM_GETISEARCHSTRINGW:
9318     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9319     return FALSE;
9320
9321   case LVM_GETITEMA:
9322     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9323
9324   case LVM_GETITEMW:
9325     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9326
9327   case LVM_GETITEMCOUNT:
9328     return infoPtr->nItemCount;
9329
9330   case LVM_GETITEMPOSITION:
9331     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9332
9333   case LVM_GETITEMRECT:
9334     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9335
9336   case LVM_GETITEMSPACING:
9337     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9338
9339   case LVM_GETITEMSTATE:
9340     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9341
9342   case LVM_GETITEMTEXTA:
9343     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9344
9345   case LVM_GETITEMTEXTW:
9346     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9347
9348   case LVM_GETNEXTITEM:
9349     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9350
9351   case LVM_GETNUMBEROFWORKAREAS:
9352     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9353     return 1;
9354
9355   case LVM_GETORIGIN:
9356     if (!lParam) return FALSE;
9357     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9358     return TRUE;
9359
9360   /* case LVM_GETOUTLINECOLOR: */
9361
9362   /* case LVM_GETSELECTEDCOLUMN: */
9363
9364   case LVM_GETSELECTEDCOUNT:
9365     return LISTVIEW_GetSelectedCount(infoPtr);
9366
9367   case LVM_GETSELECTIONMARK:
9368     return infoPtr->nSelectionMark;
9369
9370   case LVM_GETSTRINGWIDTHA:
9371     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9372
9373   case LVM_GETSTRINGWIDTHW:
9374     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9375
9376   case LVM_GETSUBITEMRECT:
9377     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9378
9379   case LVM_GETTEXTBKCOLOR:
9380     return infoPtr->clrTextBk;
9381
9382   case LVM_GETTEXTCOLOR:
9383     return infoPtr->clrText;
9384
9385   /* case LVM_GETTILEINFO: */
9386
9387   /* case LVM_GETTILEVIEWINFO: */
9388
9389   case LVM_GETTOOLTIPS:
9390     if( !infoPtr->hwndToolTip )
9391         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9392     return (LRESULT)infoPtr->hwndToolTip;
9393
9394   case LVM_GETTOPINDEX:
9395     return LISTVIEW_GetTopIndex(infoPtr);
9396
9397   /*case LVM_GETUNICODEFORMAT:
9398     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9399     return FALSE;*/
9400
9401   /* case LVM_GETVIEW: */
9402
9403   case LVM_GETVIEWRECT:
9404     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9405
9406   case LVM_GETWORKAREAS:
9407     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9408     return FALSE;
9409
9410   /* case LVM_HASGROUP: */
9411
9412   case LVM_HITTEST:
9413     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9414
9415   case LVM_INSERTCOLUMNA:
9416     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9417
9418   case LVM_INSERTCOLUMNW:
9419     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9420
9421   /* case LVM_INSERTGROUP: */
9422
9423   /* case LVM_INSERTGROUPSORTED: */
9424
9425   case LVM_INSERTITEMA:
9426     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9427
9428   case LVM_INSERTITEMW:
9429     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9430
9431   /* case LVM_INSERTMARKHITTEST: */
9432
9433   /* case LVM_ISGROUPVIEWENABLED: */
9434
9435   /* case LVM_MAPIDTOINDEX: */
9436
9437   /* case LVM_MAPINDEXTOID: */
9438
9439   /* case LVM_MOVEGROUP: */
9440
9441   /* case LVM_MOVEITEMTOGROUP: */
9442
9443   case LVM_REDRAWITEMS:
9444     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9445
9446   /* case LVM_REMOVEALLGROUPS: */
9447
9448   /* case LVM_REMOVEGROUP: */
9449
9450   case LVM_SCROLL:
9451     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9452
9453   case LVM_SETBKCOLOR:
9454     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9455
9456   /* case LVM_SETBKIMAGE: */
9457
9458   case LVM_SETCALLBACKMASK:
9459     infoPtr->uCallbackMask = (UINT)wParam;
9460     return TRUE;
9461
9462   case LVM_SETCOLUMNA:
9463     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9464
9465   case LVM_SETCOLUMNW:
9466     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9467
9468   case LVM_SETCOLUMNORDERARRAY:
9469     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9470
9471   case LVM_SETCOLUMNWIDTH:
9472     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9473
9474   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9475     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9476
9477   /* case LVM_SETGROUPINFO: */
9478
9479   /* case LVM_SETGROUPMETRICS: */
9480
9481   case LVM_SETHOTCURSOR:
9482     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9483
9484   case LVM_SETHOTITEM:
9485     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9486
9487   case LVM_SETHOVERTIME:
9488     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9489
9490   case LVM_SETICONSPACING:
9491     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9492
9493   case LVM_SETIMAGELIST:
9494     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9495
9496   /* case LVM_SETINFOTIP: */
9497
9498   /* case LVM_SETINSERTMARK: */
9499
9500   /* case LVM_SETINSERTMARKCOLOR: */
9501
9502   case LVM_SETITEMA:
9503     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9504
9505   case LVM_SETITEMW:
9506     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9507
9508   case LVM_SETITEMCOUNT:
9509     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9510
9511   case LVM_SETITEMPOSITION:
9512     {
9513         POINT pt;
9514         pt.x = (short)LOWORD(lParam);
9515         pt.y = (short)HIWORD(lParam);
9516         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9517     }
9518
9519   case LVM_SETITEMPOSITION32:
9520     if (lParam == 0) return FALSE;
9521     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9522
9523   case LVM_SETITEMSTATE:
9524     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9525
9526   case LVM_SETITEMTEXTA:
9527     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9528
9529   case LVM_SETITEMTEXTW:
9530     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9531
9532   /* case LVM_SETOUTLINECOLOR: */
9533
9534   /* case LVM_SETSELECTEDCOLUMN: */
9535
9536   case LVM_SETSELECTIONMARK:
9537     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9538
9539   case LVM_SETTEXTBKCOLOR:
9540     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9541
9542   case LVM_SETTEXTCOLOR:
9543     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9544
9545   /* case LVM_SETTILEINFO: */
9546
9547   /* case LVM_SETTILEVIEWINFO: */
9548
9549   /* case LVM_SETTILEWIDTH: */
9550
9551   case LVM_SETTOOLTIPS:
9552     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9553
9554   /* case LVM_SETUNICODEFORMAT: */
9555
9556   /* case LVM_SETVIEW: */
9557
9558   /* case LVM_SETWORKAREAS: */
9559
9560   /* case LVM_SORTGROUPS: */
9561
9562   case LVM_SORTITEMS:
9563     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9564
9565   /* LVM_SORTITEMSEX: */
9566
9567   case LVM_SUBITEMHITTEST:
9568     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9569
9570   case LVM_UPDATE:
9571     return LISTVIEW_Update(infoPtr, (INT)wParam);
9572
9573   case WM_CHAR:
9574     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9575
9576   case WM_COMMAND:
9577     return LISTVIEW_Command(infoPtr, wParam, lParam);
9578
9579   case WM_CREATE:
9580     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9581
9582   case WM_DESTROY:
9583     return LISTVIEW_Destroy(infoPtr);
9584
9585   case WM_ENABLE:
9586     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9587
9588   case WM_ERASEBKGND:
9589     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9590
9591   case WM_GETDLGCODE:
9592     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9593
9594   case WM_GETFONT:
9595     return (LRESULT)infoPtr->hFont;
9596
9597   case WM_HSCROLL:
9598     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9599
9600   case WM_KEYDOWN:
9601     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9602
9603   case WM_KILLFOCUS:
9604     return LISTVIEW_KillFocus(infoPtr);
9605
9606   case WM_LBUTTONDBLCLK:
9607     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9608
9609   case WM_LBUTTONDOWN:
9610     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9611
9612   case WM_LBUTTONUP:
9613     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9614
9615   case WM_MOUSEMOVE:
9616     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9617
9618   case WM_MOUSEHOVER:
9619     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9620
9621   case WM_NCDESTROY:
9622     return LISTVIEW_NCDestroy(infoPtr);
9623
9624   case WM_NCPAINT:
9625     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9626         return 0;
9627     goto fwd_msg;
9628
9629   case WM_NOTIFY:
9630     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9631         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9632     else return 0;
9633
9634   case WM_NOTIFYFORMAT:
9635     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9636
9637   case WM_PRINTCLIENT:
9638     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9639
9640   case WM_PAINT:
9641     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9642
9643   case WM_RBUTTONDBLCLK:
9644     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9645
9646   case WM_RBUTTONDOWN:
9647     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9648
9649   case WM_RBUTTONUP:
9650     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9651
9652   case WM_SETCURSOR:
9653     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9654       return TRUE;
9655     goto fwd_msg;
9656
9657   case WM_SETFOCUS:
9658     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9659
9660   case WM_SETFONT:
9661     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9662
9663   case WM_SETREDRAW:
9664     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9665
9666   case WM_SIZE:
9667     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9668
9669   case WM_STYLECHANGED:
9670     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9671
9672   case WM_SYSCOLORCHANGE:
9673     COMCTL32_RefreshSysColors();
9674     return 0;
9675
9676 /*      case WM_TIMER: */
9677   case WM_THEMECHANGED:
9678     return LISTVIEW_ThemeChanged(infoPtr);
9679
9680   case WM_VSCROLL:
9681     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9682
9683   case WM_MOUSEWHEEL:
9684       if (wParam & (MK_SHIFT | MK_CONTROL))
9685           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9686       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9687
9688   case WM_WINDOWPOSCHANGED:
9689       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
9690       {
9691       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9692           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9693                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9694
9695       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9696       {
9697           MEASUREITEMSTRUCT mis;
9698           mis.CtlType = ODT_LISTVIEW;
9699           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9700           mis.itemID = -1;
9701           mis.itemWidth = 0;
9702           mis.itemData = 0;
9703           mis.itemHeight= infoPtr->nItemHeight;
9704           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9705           if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9706               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9707       }
9708
9709           LISTVIEW_UpdateSize(infoPtr);
9710           LISTVIEW_UpdateScroll(infoPtr);
9711       }
9712       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9713
9714 /*      case WM_WININICHANGE: */
9715
9716   default:
9717     if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9718       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9719
9720   fwd_msg:
9721     /* call default window procedure */
9722     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9723   }
9724
9725 }
9726
9727 /***
9728  * DESCRIPTION:
9729  * Registers the window class.
9730  *
9731  * PARAMETER(S):
9732  * None
9733  *
9734  * RETURN:
9735  * None
9736  */
9737 void LISTVIEW_Register(void)
9738 {
9739     WNDCLASSW wndClass;
9740
9741     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9742     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9743     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9744     wndClass.cbClsExtra = 0;
9745     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9746     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9747     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9748     wndClass.lpszClassName = WC_LISTVIEWW;
9749     RegisterClassW(&wndClass);
9750 }
9751
9752 /***
9753  * DESCRIPTION:
9754  * Unregisters the window class.
9755  *
9756  * PARAMETER(S):
9757  * None
9758  *
9759  * RETURN:
9760  * None
9761  */
9762 void LISTVIEW_Unregister(void)
9763 {
9764     UnregisterClassW(WC_LISTVIEWW, NULL);
9765 }
9766
9767 /***
9768  * DESCRIPTION:
9769  * Handle any WM_COMMAND messages
9770  *
9771  * PARAMETER(S):
9772  * [I] infoPtr : valid pointer to the listview structure
9773  * [I] wParam : the first message parameter
9774  * [I] lParam : the second message parameter
9775  *
9776  * RETURN:
9777  *   Zero.
9778  */
9779 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9780 {
9781     switch (HIWORD(wParam))
9782     {
9783         case EN_UPDATE:
9784         {
9785             /*
9786              * Adjust the edit window size
9787              */
9788             WCHAR buffer[1024];
9789             HDC           hdc = GetDC(infoPtr->hwndEdit);
9790             HFONT         hFont, hOldFont = 0;
9791             RECT          rect;
9792             SIZE          sz;
9793             int           len;
9794
9795             if (!infoPtr->hwndEdit || !hdc) return 0;
9796             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9797             GetWindowRect(infoPtr->hwndEdit, &rect);
9798
9799             /* Select font to get the right dimension of the string */
9800             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9801             if(hFont != 0)
9802             {
9803                 hOldFont = SelectObject(hdc, hFont);
9804             }
9805
9806             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9807             {
9808                 TEXTMETRICW textMetric;
9809
9810                 /* Add Extra spacing for the next character */
9811                 GetTextMetricsW(hdc, &textMetric);
9812                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9813
9814                 SetWindowPos (
9815                     infoPtr->hwndEdit,
9816                     HWND_TOP,
9817                     0,
9818                     0,
9819                     sz.cx,
9820                     rect.bottom - rect.top,
9821                     SWP_DRAWFRAME|SWP_NOMOVE);
9822             }
9823             if(hFont != 0)
9824                 SelectObject(hdc, hOldFont);
9825
9826             ReleaseDC(infoPtr->hwndEdit, hdc);
9827
9828             break;
9829         }
9830
9831         default:
9832           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9833     }
9834
9835     return 0;
9836 }
9837
9838
9839 /***
9840  * DESCRIPTION:
9841  * Subclassed edit control windproc function
9842  *
9843  * PARAMETER(S):
9844  * [I] hwnd : the edit window handle
9845  * [I] uMsg : the message that is to be processed
9846  * [I] wParam : first message parameter
9847  * [I] lParam : second message parameter
9848  * [I] isW : TRUE if input is Unicode
9849  *
9850  * RETURN:
9851  *   Zero.
9852  */
9853 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9854 {
9855     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9856     BOOL cancel = FALSE;
9857
9858     TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9859           hwnd, uMsg, wParam, lParam, isW);
9860
9861     switch (uMsg)
9862     {
9863         case WM_GETDLGCODE:
9864           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9865
9866         case WM_KILLFOCUS:
9867             break;
9868
9869         case WM_DESTROY:
9870         {
9871             WNDPROC editProc = infoPtr->EditWndProc;
9872             infoPtr->EditWndProc = 0;
9873             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9874             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9875         }
9876
9877         case WM_KEYDOWN:
9878             if (VK_ESCAPE == (INT)wParam)
9879             {
9880                 cancel = TRUE;
9881                 break;
9882             }
9883             else if (VK_RETURN == (INT)wParam)
9884                 break;
9885
9886         default:
9887             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9888     }
9889
9890     /* kill the edit */
9891     if (infoPtr->hwndEdit)
9892     {
9893         LPWSTR buffer = NULL;
9894
9895         infoPtr->hwndEdit = 0;
9896         if (!cancel)
9897         {
9898             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9899
9900             if (len)
9901             {
9902                 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9903                 {
9904                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
9905                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9906                 }
9907             }
9908         }
9909         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9910
9911         if (buffer) Free(buffer);
9912
9913     }
9914
9915     SendMessageW(hwnd, WM_CLOSE, 0, 0);
9916     return 0;
9917 }
9918
9919 /***
9920  * DESCRIPTION:
9921  * Subclassed edit control Unicode windproc function
9922  *
9923  * PARAMETER(S):
9924  * [I] hwnd : the edit window handle
9925  * [I] uMsg : the message that is to be processed
9926  * [I] wParam : first message parameter
9927  * [I] lParam : second message parameter
9928  *
9929  * RETURN:
9930  */
9931 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9932 {
9933     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9934 }
9935
9936 /***
9937  * DESCRIPTION:
9938  * Subclassed edit control ANSI windproc function
9939  *
9940  * PARAMETER(S):
9941  * [I] hwnd : the edit window handle
9942  * [I] uMsg : the message that is to be processed
9943  * [I] wParam : first message parameter
9944  * [I] lParam : second message parameter
9945  *
9946  * RETURN:
9947  */
9948 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9949 {
9950     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9951 }
9952
9953 /***
9954  * DESCRIPTION:
9955  * Creates a subclassed edit cotrol
9956  *
9957  * PARAMETER(S):
9958  * [I] infoPtr : valid pointer to the listview structure
9959  * [I] text : initial text for the edit
9960  * [I] style : the window style
9961  * [I] isW : TRUE if input is Unicode
9962  *
9963  * RETURN:
9964  */
9965 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9966         INT x, INT y, INT width, INT height, BOOL isW)
9967 {
9968     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9969     HWND hedit;
9970     SIZE sz;
9971     HDC hdc;
9972     HDC hOldFont=0;
9973     TEXTMETRICW textMetric;
9974     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9975
9976     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9977
9978     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9979     hdc = GetDC(infoPtr->hwndSelf);
9980
9981     /* Select the font to get appropriate metric dimensions */
9982     if(infoPtr->hFont != 0)
9983         hOldFont = SelectObject(hdc, infoPtr->hFont);
9984
9985     /*Get String Length in pixels */
9986     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9987
9988     /*Add Extra spacing for the next character */
9989     GetTextMetricsW(hdc, &textMetric);
9990     sz.cx += (textMetric.tmMaxCharWidth * 2);
9991
9992     if(infoPtr->hFont != 0)
9993         SelectObject(hdc, hOldFont);
9994
9995     ReleaseDC(infoPtr->hwndSelf, hdc);
9996     if (isW)
9997         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9998     else
9999         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10000
10001     if (!hedit) return 0;
10002
10003     infoPtr->EditWndProc = (WNDPROC)
10004         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10005                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10006
10007     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10008
10009     return hedit;
10010 }