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