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