shell32: Check for internal shell classes before querying the registry in SHCoCreateI...
[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     LPWSTR strW;
5044
5045     memcpy(&fiw, lpFindInfo, sizeof(fiw));
5046     if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5047     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5048     if (hasText) textfreeT(strW, FALSE);
5049     return res;
5050 }
5051
5052 /***
5053  * DESCRIPTION:
5054  * Retrieves the background image of the listview control.
5055  *
5056  * PARAMETER(S):
5057  * [I] infoPtr : valid pointer to the listview structure
5058  * [O] lpBkImage : background image attributes
5059  *
5060  * RETURN:
5061  *   SUCCESS : TRUE
5062  *   FAILURE : FALSE
5063  */
5064 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
5065 /* {   */
5066 /*   FIXME (listview, "empty stub!\n"); */
5067 /*   return FALSE;   */
5068 /* }   */
5069
5070 /***
5071  * DESCRIPTION:
5072  * Retrieves column attributes.
5073  *
5074  * PARAMETER(S):
5075  * [I] infoPtr : valid pointer to the listview structure
5076  * [I] nColumn :  column index
5077  * [IO] lpColumn : column information
5078  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5079  *           otherwise it is in fact a LPLVCOLUMNA
5080  *
5081  * RETURN:
5082  *   SUCCESS : TRUE
5083  *   FAILURE : FALSE
5084  */
5085 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5086 {
5087     COLUMN_INFO *lpColumnInfo;
5088     HDITEMW hdi;
5089
5090     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5091     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5092
5093     /* initialize memory */
5094     ZeroMemory(&hdi, sizeof(hdi));
5095
5096     if (lpColumn->mask & LVCF_TEXT)
5097     {
5098         hdi.mask |= HDI_TEXT;
5099         hdi.pszText = lpColumn->pszText;
5100         hdi.cchTextMax = lpColumn->cchTextMax;
5101     }
5102
5103     if (lpColumn->mask & LVCF_IMAGE)
5104         hdi.mask |= HDI_IMAGE;
5105
5106     if (lpColumn->mask & LVCF_ORDER)
5107         hdi.mask |= HDI_ORDER;
5108
5109     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5110
5111     if (lpColumn->mask & LVCF_FMT)
5112         lpColumn->fmt = lpColumnInfo->fmt;
5113
5114     if (lpColumn->mask & LVCF_WIDTH)
5115         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5116
5117     if (lpColumn->mask & LVCF_IMAGE)
5118         lpColumn->iImage = hdi.iImage;
5119
5120     if (lpColumn->mask & LVCF_ORDER)
5121         lpColumn->iOrder = hdi.iOrder;
5122
5123     return TRUE;
5124 }
5125
5126
5127 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5128 {
5129     INT i;
5130
5131     if (!lpiArray)
5132         return FALSE;
5133
5134     /* FIXME: little hack */
5135     for (i = 0; i < iCount; i++)
5136         lpiArray[i] = i;
5137
5138     return TRUE;
5139 }
5140
5141 /***
5142  * DESCRIPTION:
5143  * Retrieves the column width.
5144  *
5145  * PARAMETER(S):
5146  * [I] infoPtr : valid pointer to the listview structure
5147  * [I] int : column index
5148  *
5149  * RETURN:
5150  *   SUCCESS : column width
5151  *   FAILURE : zero
5152  */
5153 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5154 {
5155     INT nColumnWidth = 0;
5156     HDITEMW hdItem;
5157
5158     TRACE("nColumn=%d\n", nColumn);
5159
5160     /* we have a 'column' in LIST and REPORT mode only */
5161     switch(infoPtr->dwStyle & LVS_TYPEMASK)
5162     {
5163     case LVS_LIST:
5164         nColumnWidth = infoPtr->nItemWidth;
5165         break;
5166     case LVS_REPORT:
5167         /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5168          * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5169          * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5170          *
5171          * TODO: should we do the same in LVM_GETCOLUMN?
5172          */
5173         hdItem.mask = HDI_WIDTH;
5174         if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5175         {
5176             WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5177             return 0;
5178         }
5179         nColumnWidth = hdItem.cxy;
5180         break;
5181     }
5182
5183     TRACE("nColumnWidth=%d\n", nColumnWidth);
5184     return nColumnWidth;
5185 }
5186
5187 /***
5188  * DESCRIPTION:
5189  * In list or report display mode, retrieves the number of items that can fit
5190  * vertically in the visible area. In icon or small icon display mode,
5191  * retrieves the total number of visible items.
5192  *
5193  * PARAMETER(S):
5194  * [I] infoPtr : valid pointer to the listview structure
5195  *
5196  * RETURN:
5197  * Number of fully visible items.
5198  */
5199 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5200 {
5201     switch (infoPtr->dwStyle & LVS_TYPEMASK)
5202     {
5203     case LVS_ICON:
5204     case LVS_SMALLICON:
5205         return infoPtr->nItemCount;
5206     case LVS_REPORT:
5207         return LISTVIEW_GetCountPerColumn(infoPtr);
5208     case LVS_LIST:
5209         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5210     }
5211     assert(FALSE);
5212     return 0;
5213 }
5214
5215 /***
5216  * DESCRIPTION:
5217  * Retrieves an image list handle.
5218  *
5219  * PARAMETER(S):
5220  * [I] infoPtr : valid pointer to the listview structure
5221  * [I] nImageList : image list identifier
5222  *
5223  * RETURN:
5224  *   SUCCESS : image list handle
5225  *   FAILURE : NULL
5226  */
5227 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5228 {
5229     switch (nImageList)
5230     {
5231     case LVSIL_NORMAL: return infoPtr->himlNormal;
5232     case LVSIL_SMALL: return infoPtr->himlSmall;
5233     case LVSIL_STATE: return infoPtr->himlState;
5234     }
5235     return NULL;
5236 }
5237
5238 /* LISTVIEW_GetISearchString */
5239
5240 /***
5241  * DESCRIPTION:
5242  * Retrieves item attributes.
5243  *
5244  * PARAMETER(S):
5245  * [I] hwnd : window handle
5246  * [IO] lpLVItem : item info
5247  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5248  *           if FALSE, then lpLVItem is a LPLVITEMA.
5249  *
5250  * NOTE:
5251  *   This is the internal 'GetItem' interface -- it tries to
5252  *   be smart and avoid text copies, if possible, by modifying
5253  *   lpLVItem->pszText to point to the text string. Please note
5254  *   that this is not always possible (e.g. OWNERDATA), so on
5255  *   entry you *must* supply valid values for pszText, and cchTextMax.
5256  *   The only difference to the documented interface is that upon
5257  *   return, you should use *only* the lpLVItem->pszText, rather than
5258  *   the buffer pointer you provided on input. Most code already does
5259  *   that, so it's not a problem.
5260  *   For the two cases when the text must be copied (that is,
5261  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5262  *
5263  * RETURN:
5264  *   SUCCESS : TRUE
5265  *   FAILURE : FALSE
5266  */
5267 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5268 {
5269     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5270     NMLVDISPINFOW dispInfo;
5271     ITEM_INFO *lpItem;
5272     ITEMHDR* pItemHdr;
5273     HDPA hdpaSubItems;
5274     INT isubitem;
5275
5276     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5277
5278     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5279         return FALSE;
5280
5281     if (lpLVItem->mask == 0) return TRUE;
5282
5283     /* make a local copy */
5284     isubitem = lpLVItem->iSubItem;
5285
5286     /* a quick optimization if all we're asked is the focus state
5287      * these queries are worth optimising since they are common,
5288      * and can be answered in constant time, without the heavy accesses */
5289     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5290          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5291     {
5292         lpLVItem->state = 0;
5293         if (infoPtr->nFocusedItem == lpLVItem->iItem)
5294             lpLVItem->state |= LVIS_FOCUSED;
5295         return TRUE;
5296     }
5297
5298     ZeroMemory(&dispInfo, sizeof(dispInfo));
5299
5300     /* if the app stores all the data, handle it separately */
5301     if (infoPtr->dwStyle & LVS_OWNERDATA)
5302     {
5303         dispInfo.item.state = 0;
5304
5305         /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5306         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5307         {
5308             /* NOTE: copy only fields which we _know_ are initialized, some apps
5309              *       depend on the uninitialized fields being 0 */
5310             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5311             dispInfo.item.iItem = lpLVItem->iItem;
5312             dispInfo.item.iSubItem = isubitem;
5313             if (lpLVItem->mask & LVIF_TEXT)
5314             {
5315                 dispInfo.item.pszText = lpLVItem->pszText;
5316                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;                
5317             }
5318             if (lpLVItem->mask & LVIF_STATE)
5319                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5320             notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5321             dispInfo.item.stateMask = lpLVItem->stateMask;
5322             if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5323             {
5324                 /* full size structure expected - _WIN32IE >= 0x560 */
5325                 *lpLVItem = dispInfo.item;
5326             }
5327             else if (lpLVItem->mask & LVIF_INDENT)
5328             {
5329                 /* indent member expected - _WIN32IE >= 0x300 */
5330                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5331             }
5332             else
5333             {
5334                 /* minimal structure expected */
5335                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5336             }
5337             TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5338         }
5339         
5340         /* make sure lParam is zeroed out */
5341         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5342         
5343         /* we store only a little state, so if we're not asked, we're done */
5344         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5345
5346         /* if focus is handled by us, report it */
5347         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5348         {
5349             lpLVItem->state &= ~LVIS_FOCUSED;
5350             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5351                 lpLVItem->state |= LVIS_FOCUSED;
5352         }
5353
5354         /* and do the same for selection, if we handle it */
5355         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5356         {
5357             lpLVItem->state &= ~LVIS_SELECTED;
5358             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5359                 lpLVItem->state |= LVIS_SELECTED;
5360         }
5361         
5362         return TRUE;
5363     }
5364
5365     /* find the item and subitem structures before we proceed */
5366     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5367     lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5368     assert (lpItem);
5369
5370     if (isubitem)
5371     {
5372         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5373         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5374         if (!lpSubItem)
5375         {
5376             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5377             isubitem = 0;
5378         }
5379     }
5380     else
5381         pItemHdr = &lpItem->hdr;
5382
5383     /* Do we need to query the state from the app? */
5384     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5385     {
5386         dispInfo.item.mask |= LVIF_STATE;
5387         dispInfo.item.stateMask = infoPtr->uCallbackMask;
5388     }
5389   
5390     /* Do we need to enquire about the image? */
5391     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5392         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5393     {
5394         dispInfo.item.mask |= LVIF_IMAGE;
5395         dispInfo.item.iImage = I_IMAGECALLBACK;
5396     }
5397
5398     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5399     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5400     {
5401         dispInfo.item.mask |= LVIF_TEXT;
5402         dispInfo.item.pszText = lpLVItem->pszText;
5403         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5404         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5405             *dispInfo.item.pszText = '\0';
5406     }
5407
5408     /* If we don't have all the requested info, query the application */
5409     if (dispInfo.item.mask != 0)
5410     {
5411         dispInfo.item.iItem = lpLVItem->iItem;
5412         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5413         dispInfo.item.lParam = lpItem->lParam;
5414         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5415         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5416     }
5417
5418     /* we should not store values for subitems */
5419     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5420
5421     /* Now, handle the iImage field */
5422     if (dispInfo.item.mask & LVIF_IMAGE)
5423     {
5424         lpLVItem->iImage = dispInfo.item.iImage;
5425         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5426             pItemHdr->iImage = dispInfo.item.iImage;
5427     }
5428     else if (lpLVItem->mask & LVIF_IMAGE)
5429     {
5430         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5431             lpLVItem->iImage = pItemHdr->iImage;
5432         else
5433             lpLVItem->iImage = 0;
5434     }
5435
5436     /* The pszText field */
5437     if (dispInfo.item.mask & LVIF_TEXT)
5438     {
5439         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5440             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5441
5442         lpLVItem->pszText = dispInfo.item.pszText;
5443     }
5444     else if (lpLVItem->mask & LVIF_TEXT)
5445     {
5446         if (isW) lpLVItem->pszText = pItemHdr->pszText;
5447         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5448     }
5449
5450     /* Next is the lParam field */
5451     if (dispInfo.item.mask & LVIF_PARAM)
5452     {
5453         lpLVItem->lParam = dispInfo.item.lParam;
5454         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5455             lpItem->lParam = dispInfo.item.lParam;
5456     }
5457     else if (lpLVItem->mask & LVIF_PARAM)
5458         lpLVItem->lParam = lpItem->lParam;
5459
5460     /* if this is a subitem, we're done */
5461     if (isubitem) return TRUE;
5462
5463     /* ... the state field (this one is different due to uCallbackmask) */
5464     if (lpLVItem->mask & LVIF_STATE)
5465     {
5466         lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5467         if (dispInfo.item.mask & LVIF_STATE)
5468         {
5469             lpLVItem->state &= ~dispInfo.item.stateMask;
5470             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5471         }
5472         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5473         {
5474             lpLVItem->state &= ~LVIS_FOCUSED;
5475             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5476                 lpLVItem->state |= LVIS_FOCUSED;
5477         }
5478         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5479         {
5480             lpLVItem->state &= ~LVIS_SELECTED;
5481             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5482                 lpLVItem->state |= LVIS_SELECTED;
5483         }           
5484     }
5485
5486     /* and last, but not least, the indent field */
5487     if (lpLVItem->mask & LVIF_INDENT)
5488         lpLVItem->iIndent = lpItem->iIndent;
5489
5490     return TRUE;
5491 }
5492
5493 /***
5494  * DESCRIPTION:
5495  * Retrieves item attributes.
5496  *
5497  * PARAMETER(S):
5498  * [I] hwnd : window handle
5499  * [IO] lpLVItem : item info
5500  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5501  *           if FALSE, then lpLVItem is a LPLVITEMA.
5502  *
5503  * NOTE:
5504  *   This is the external 'GetItem' interface -- it properly copies
5505  *   the text in the provided buffer.
5506  *
5507  * RETURN:
5508  *   SUCCESS : TRUE
5509  *   FAILURE : FALSE
5510  */
5511 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5512 {
5513     LPWSTR pszText;
5514     BOOL bResult;
5515
5516     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5517         return FALSE;
5518
5519     pszText = lpLVItem->pszText;
5520     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5521     if (bResult && lpLVItem->pszText != pszText)
5522         textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5523     lpLVItem->pszText = pszText;
5524
5525     return bResult;
5526 }
5527
5528
5529 /***
5530  * DESCRIPTION:
5531  * Retrieves the position (upper-left) of the listview control item.
5532  * Note that for LVS_ICON style, the upper-left is that of the icon
5533  * and not the bounding box.
5534  *
5535  * PARAMETER(S):
5536  * [I] infoPtr : valid pointer to the listview structure
5537  * [I] nItem : item index
5538  * [O] lpptPosition : coordinate information
5539  *
5540  * RETURN:
5541  *   SUCCESS : TRUE
5542  *   FAILURE : FALSE
5543  */
5544 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5545 {
5546     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5547     POINT Origin;
5548
5549     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5550
5551     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5552
5553     LISTVIEW_GetOrigin(infoPtr, &Origin);
5554     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5555
5556     if (uView == LVS_ICON)
5557     {
5558         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5559         lpptPosition->y += ICON_TOP_PADDING;
5560     }
5561     lpptPosition->x += Origin.x;
5562     lpptPosition->y += Origin.y;
5563     
5564     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5565     return TRUE;
5566 }
5567
5568
5569 /***
5570  * DESCRIPTION:
5571  * Retrieves the bounding rectangle for a listview control item.
5572  *
5573  * PARAMETER(S):
5574  * [I] infoPtr : valid pointer to the listview structure
5575  * [I] nItem : item index
5576  * [IO] lprc : bounding rectangle coordinates
5577  *     lprc->left specifies the portion of the item for which the bounding
5578  *     rectangle will be retrieved.
5579  *
5580  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5581  *        including the icon and label.
5582  *         *
5583  *         * For LVS_ICON
5584  *         * Experiment shows that native control returns:
5585  *         *  width = min (48, length of text line)
5586  *         *    .left = position.x - (width - iconsize.cx)/2
5587  *         *    .right = .left + width
5588  *         *  height = #lines of text * ntmHeight + icon height + 8
5589  *         *    .top = position.y - 2
5590  *         *    .bottom = .top + height
5591  *         *  separation between items .y = itemSpacing.cy - height
5592  *         *                           .x = itemSpacing.cx - width
5593  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5594  *         *
5595  *         * For LVS_ICON
5596  *         * Experiment shows that native control returns:
5597  *         *  width = iconSize.cx + 16
5598  *         *    .left = position.x - (width - iconsize.cx)/2
5599  *         *    .right = .left + width
5600  *         *  height = iconSize.cy + 4
5601  *         *    .top = position.y - 2
5602  *         *    .bottom = .top + height
5603  *         *  separation between items .y = itemSpacing.cy - height
5604  *         *                           .x = itemSpacing.cx - width
5605  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5606  *         *
5607  *         * For LVS_ICON
5608  *         * Experiment shows that native control returns:
5609  *         *  width = text length
5610  *         *    .left = position.x - width/2
5611  *         *    .right = .left + width
5612  *         *  height = ntmH * linecount + 2
5613  *         *    .top = position.y + iconSize.cy + 6
5614  *         *    .bottom = .top + height
5615  *         *  separation between items .y = itemSpacing.cy - height
5616  *         *                           .x = itemSpacing.cx - width
5617  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5618  *      rectangles, but excludes columns in report view.
5619  *
5620  * RETURN:
5621  *   SUCCESS : TRUE
5622  *   FAILURE : FALSE
5623  *
5624  * NOTES
5625  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5626  *   upon whether the window has the focus currently and on whether the item
5627  *   is the one with the focus.  Ensure that the control's record of which
5628  *   item has the focus agrees with the items' records.
5629  */
5630 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5631 {
5632     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5633     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5634     BOOL doLabel = TRUE, oversizedBox = FALSE;
5635     POINT Position, Origin;
5636     LVITEMW lvItem;
5637
5638     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5639
5640     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5641
5642     LISTVIEW_GetOrigin(infoPtr, &Origin);
5643     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5644
5645     /* Be smart and try to figure out the minimum we have to do */
5646     if (lprc->left == LVIR_ICON) doLabel = FALSE;
5647     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5648     if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5649         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5650         oversizedBox = TRUE;
5651
5652     /* get what we need from the item before hand, so we make
5653      * only one request. This can speed up things, if data
5654      * is stored on the app side */
5655     lvItem.mask = 0;
5656     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5657     if (doLabel) lvItem.mask |= LVIF_TEXT;
5658     lvItem.iItem = nItem;
5659     lvItem.iSubItem = 0;
5660     lvItem.pszText = szDispText;
5661     lvItem.cchTextMax = DISP_TEXT_SIZE;
5662     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5663     /* we got the state already up, simulate it here, to avoid a reget */
5664     if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5665     {
5666         lvItem.mask |= LVIF_STATE;
5667         lvItem.stateMask = LVIS_FOCUSED;
5668         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5669     }
5670
5671     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5672         lprc->left = LVIR_BOUNDS;
5673     switch(lprc->left)
5674     {
5675     case LVIR_ICON:
5676         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5677         break;
5678
5679     case LVIR_LABEL:
5680         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5681         break;
5682
5683     case LVIR_BOUNDS:
5684         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5685         break;
5686
5687     case LVIR_SELECTBOUNDS:
5688         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5689         break;
5690
5691     default:
5692         WARN("Unknown value: %d\n", lprc->left);
5693         return FALSE;
5694     }
5695
5696     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5697
5698     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5699
5700     return TRUE;
5701 }
5702
5703 /***
5704  * DESCRIPTION:
5705  * Retrieves the spacing between listview control items.
5706  *
5707  * PARAMETER(S):
5708  * [I] infoPtr : valid pointer to the listview structure
5709  * [IO] lprc : rectangle to receive the output
5710  *             on input, lprc->top = nSubItem
5711  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5712  * 
5713  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5714  *       not only those of the first column.
5715  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5716  * 
5717  * RETURN:
5718  *     TRUE: success
5719  *     FALSE: failure
5720  */
5721 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5722 {
5723     POINT Position;
5724     LVITEMW lvItem;
5725     INT nColumn;
5726     
5727     if (!lprc) return FALSE;
5728
5729     nColumn = lprc->top;
5730
5731     TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5732     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5733     if (lprc->top == 0)
5734         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5735
5736     if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5737
5738     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5739
5740     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5741
5742     lvItem.mask = 0;
5743     lvItem.iItem = nItem;
5744     lvItem.iSubItem = nColumn;
5745     
5746     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5747     switch(lprc->left)
5748     {
5749     case LVIR_ICON:
5750         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5751         break;
5752
5753     case LVIR_LABEL:
5754     case LVIR_BOUNDS:
5755         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5756         break;
5757
5758     default:
5759         ERR("Unknown bounds=%d\n", lprc->left);
5760         return FALSE;
5761     }
5762
5763     OffsetRect(lprc, Position.x, Position.y);
5764     return TRUE;
5765 }
5766
5767
5768 /***
5769  * DESCRIPTION:
5770  * Retrieves the width of a label.
5771  *
5772  * PARAMETER(S):
5773  * [I] infoPtr : valid pointer to the listview structure
5774  *
5775  * RETURN:
5776  *   SUCCESS : string width (in pixels)
5777  *   FAILURE : zero
5778  */
5779 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5780 {
5781     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5782     LVITEMW lvItem;
5783
5784     TRACE("(nItem=%d)\n", nItem);
5785
5786     lvItem.mask = LVIF_TEXT;
5787     lvItem.iItem = nItem;
5788     lvItem.iSubItem = 0;
5789     lvItem.pszText = szDispText;
5790     lvItem.cchTextMax = DISP_TEXT_SIZE;
5791     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5792   
5793     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5794 }
5795
5796 /***
5797  * DESCRIPTION:
5798  * Retrieves the spacing between listview control items.
5799  *
5800  * PARAMETER(S):
5801  * [I] infoPtr : valid pointer to the listview structure
5802  * [I] bSmall : flag for small or large icon
5803  *
5804  * RETURN:
5805  * Horizontal + vertical spacing
5806  */
5807 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
5808 {
5809   LONG lResult;
5810
5811   if (!bSmall)
5812   {
5813     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5814   }
5815   else
5816   {
5817     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5818       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5819     else
5820       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5821   }
5822   return lResult;
5823 }
5824
5825 /***
5826  * DESCRIPTION:
5827  * Retrieves the state of a listview control item.
5828  *
5829  * PARAMETER(S):
5830  * [I] infoPtr : valid pointer to the listview structure
5831  * [I] nItem : item index
5832  * [I] uMask : state mask
5833  *
5834  * RETURN:
5835  * State specified by the mask.
5836  */
5837 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5838 {
5839     LVITEMW lvItem;
5840
5841     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5842
5843     lvItem.iItem = nItem;
5844     lvItem.iSubItem = 0;
5845     lvItem.mask = LVIF_STATE;
5846     lvItem.stateMask = uMask;
5847     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5848
5849     return lvItem.state & uMask;
5850 }
5851
5852 /***
5853  * DESCRIPTION:
5854  * Retrieves the text of a listview control item or subitem.
5855  *
5856  * PARAMETER(S):
5857  * [I] hwnd : window handle
5858  * [I] nItem : item index
5859  * [IO] lpLVItem : item information
5860  * [I] isW :  TRUE if lpLVItem is Unicode
5861  *
5862  * RETURN:
5863  *   SUCCESS : string length
5864  *   FAILURE : 0
5865  */
5866 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5867 {
5868     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5869
5870     lpLVItem->mask = LVIF_TEXT;
5871     lpLVItem->iItem = nItem;
5872     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5873
5874     return textlenT(lpLVItem->pszText, isW);
5875 }
5876
5877 /***
5878  * DESCRIPTION:
5879  * Searches for an item based on properties + relationships.
5880  *
5881  * PARAMETER(S):
5882  * [I] infoPtr : valid pointer to the listview structure
5883  * [I] nItem : item index
5884  * [I] uFlags : relationship flag
5885  *
5886  * RETURN:
5887  *   SUCCESS : item index
5888  *   FAILURE : -1
5889  */
5890 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5891 {
5892     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5893     UINT uMask = 0;
5894     LVFINDINFOW lvFindInfo;
5895     INT nCountPerColumn;
5896     INT nCountPerRow;
5897     INT i;
5898
5899     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5900     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5901
5902     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5903
5904     if (uFlags & LVNI_CUT)
5905       uMask |= LVIS_CUT;
5906
5907     if (uFlags & LVNI_DROPHILITED)
5908       uMask |= LVIS_DROPHILITED;
5909
5910     if (uFlags & LVNI_FOCUSED)
5911       uMask |= LVIS_FOCUSED;
5912
5913     if (uFlags & LVNI_SELECTED)
5914       uMask |= LVIS_SELECTED;
5915
5916     /* if we're asked for the focused item, that's only one, 
5917      * so it's worth optimizing */
5918     if (uFlags & LVNI_FOCUSED)
5919     {
5920         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5921         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5922     }
5923     
5924     if (uFlags & LVNI_ABOVE)
5925     {
5926       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5927       {
5928         while (nItem >= 0)
5929         {
5930           nItem--;
5931           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5932             return nItem;
5933         }
5934       }
5935       else
5936       {
5937         /* Special case for autoarrange - move 'til the top of a list */
5938         if (is_autoarrange(infoPtr))
5939         {
5940           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5941           while (nItem - nCountPerRow >= 0)
5942           {
5943             nItem -= nCountPerRow;
5944             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5945               return nItem;
5946           }
5947           return -1;
5948         }
5949         lvFindInfo.flags = LVFI_NEARESTXY;
5950         lvFindInfo.vkDirection = VK_UP;
5951         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5952         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5953         {
5954           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5955             return nItem;
5956         }
5957       }
5958     }
5959     else if (uFlags & LVNI_BELOW)
5960     {
5961       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5962       {
5963         while (nItem < infoPtr->nItemCount)
5964         {
5965           nItem++;
5966           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5967             return nItem;
5968         }
5969       }
5970       else
5971       {
5972         /* Special case for autoarrange - move 'til the bottom of a list */
5973         if (is_autoarrange(infoPtr))
5974         {
5975           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5976           while (nItem + nCountPerRow < infoPtr->nItemCount )
5977           {
5978             nItem += nCountPerRow;
5979             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5980               return nItem;
5981           }
5982           return -1;
5983         }
5984         lvFindInfo.flags = LVFI_NEARESTXY;
5985         lvFindInfo.vkDirection = VK_DOWN;
5986         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5987         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5988         {
5989           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5990             return nItem;
5991         }
5992       }
5993     }
5994     else if (uFlags & LVNI_TOLEFT)
5995     {
5996       if (uView == LVS_LIST)
5997       {
5998         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5999         while (nItem - nCountPerColumn >= 0)
6000         {
6001           nItem -= nCountPerColumn;
6002           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6003             return nItem;
6004         }
6005       }
6006       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6007       {
6008         /* Special case for autoarrange - move 'ti the beginning of a row */
6009         if (is_autoarrange(infoPtr))
6010         {
6011           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6012           while (nItem % nCountPerRow > 0)
6013           {
6014             nItem --;
6015             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6016               return nItem;
6017           }
6018           return -1;
6019         }
6020         lvFindInfo.flags = LVFI_NEARESTXY;
6021         lvFindInfo.vkDirection = VK_LEFT;
6022         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6023         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6024         {
6025           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6026             return nItem;
6027         }
6028       }
6029     }
6030     else if (uFlags & LVNI_TORIGHT)
6031     {
6032       if (uView == LVS_LIST)
6033       {
6034         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6035         while (nItem + nCountPerColumn < infoPtr->nItemCount)
6036         {
6037           nItem += nCountPerColumn;
6038           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6039             return nItem;
6040         }
6041       }
6042       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6043       {
6044         /* Special case for autoarrange - move 'til the end of a row */
6045         if (is_autoarrange(infoPtr))
6046         {
6047           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6048           while (nItem % nCountPerRow < nCountPerRow - 1 )
6049           {
6050             nItem ++;
6051             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6052               return nItem;
6053           }
6054           return -1;
6055         }
6056         lvFindInfo.flags = LVFI_NEARESTXY;
6057         lvFindInfo.vkDirection = VK_RIGHT;
6058         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6059         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6060         {
6061           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6062             return nItem;
6063         }
6064       }
6065     }
6066     else
6067     {
6068       nItem++;
6069
6070       /* search by index */
6071       for (i = nItem; i < infoPtr->nItemCount; i++)
6072       {
6073         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6074           return i;
6075       }
6076     }
6077
6078     return -1;
6079 }
6080
6081 /* LISTVIEW_GetNumberOfWorkAreas */
6082
6083 /***
6084  * DESCRIPTION:
6085  * Retrieves the origin coordinates when in icon or small icon display mode.
6086  *
6087  * PARAMETER(S):
6088  * [I] infoPtr : valid pointer to the listview structure
6089  * [O] lpptOrigin : coordinate information
6090  *
6091  * RETURN:
6092  *   None.
6093  */
6094 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6095 {
6096     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6097     INT nHorzPos = 0, nVertPos = 0;
6098     SCROLLINFO scrollInfo;
6099
6100     scrollInfo.cbSize = sizeof(SCROLLINFO);    
6101     scrollInfo.fMask = SIF_POS;
6102     
6103     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6104         nHorzPos = scrollInfo.nPos;
6105     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6106         nVertPos = scrollInfo.nPos;
6107
6108     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6109
6110     lpptOrigin->x = infoPtr->rcList.left;
6111     lpptOrigin->y = infoPtr->rcList.top;
6112     if (uView == LVS_LIST)
6113         nHorzPos *= infoPtr->nItemWidth;
6114     else if (uView == LVS_REPORT)
6115         nVertPos *= infoPtr->nItemHeight;
6116     
6117     lpptOrigin->x -= nHorzPos;
6118     lpptOrigin->y -= nVertPos;
6119
6120     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6121 }
6122
6123 /***
6124  * DESCRIPTION:
6125  * Retrieves the width of a string.
6126  *
6127  * PARAMETER(S):
6128  * [I] hwnd : window handle
6129  * [I] lpszText : text string to process
6130  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6131  *
6132  * RETURN:
6133  *   SUCCESS : string width (in pixels)
6134  *   FAILURE : zero
6135  */
6136 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6137 {
6138     SIZE stringSize;
6139     
6140     stringSize.cx = 0;    
6141     if (is_textT(lpszText, isW))
6142     {
6143         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6144         HDC hdc = GetDC(infoPtr->hwndSelf);
6145         HFONT hOldFont = SelectObject(hdc, hFont);
6146
6147         if (isW)
6148             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6149         else
6150             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6151         SelectObject(hdc, hOldFont);
6152         ReleaseDC(infoPtr->hwndSelf, hdc);
6153     }
6154     return stringSize.cx;
6155 }
6156
6157 /***
6158  * DESCRIPTION:
6159  * Determines which listview item is located at the specified position.
6160  *
6161  * PARAMETER(S):
6162  * [I] infoPtr : valid pointer to the listview structure
6163  * [IO] lpht : hit test information
6164  * [I] subitem : fill out iSubItem.
6165  * [I] select : return the index only if the hit selects the item
6166  *
6167  * NOTE:
6168  * (mm 20001022): We must not allow iSubItem to be touched, for
6169  * an app might pass only a structure with space up to iItem!
6170  * (MS Office 97 does that for instance in the file open dialog)
6171  * 
6172  * RETURN:
6173  *   SUCCESS : item index
6174  *   FAILURE : -1
6175  */
6176 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6177 {
6178     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6179     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6180     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6181     POINT Origin, Position, opt;
6182     LVITEMW lvItem;
6183     ITERATOR i;
6184     INT iItem;
6185     
6186     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6187     
6188     lpht->flags = 0;
6189     lpht->iItem = -1;
6190     if (subitem) lpht->iSubItem = 0;
6191
6192     if (infoPtr->rcList.left > lpht->pt.x)
6193         lpht->flags |= LVHT_TOLEFT;
6194     else if (infoPtr->rcList.right < lpht->pt.x)
6195         lpht->flags |= LVHT_TORIGHT;
6196     
6197     if (infoPtr->rcList.top > lpht->pt.y)
6198         lpht->flags |= LVHT_ABOVE;
6199     else if (infoPtr->rcList.bottom < lpht->pt.y)
6200         lpht->flags |= LVHT_BELOW;
6201
6202     TRACE("lpht->flags=0x%x\n", lpht->flags);
6203     if (lpht->flags) return -1;
6204
6205     lpht->flags |= LVHT_NOWHERE;
6206
6207     LISTVIEW_GetOrigin(infoPtr, &Origin);
6208    
6209     /* first deal with the large items */
6210     rcSearch.left = lpht->pt.x;
6211     rcSearch.top = lpht->pt.y;
6212     rcSearch.right = rcSearch.left + 1;
6213     rcSearch.bottom = rcSearch.top + 1;
6214     
6215     iterator_frameditems(&i, infoPtr, &rcSearch);
6216     iterator_next(&i); /* go to first item in the sequence */
6217     iItem = i.nItem;
6218     iterator_destroy(&i);
6219    
6220     TRACE("lpht->iItem=%d\n", iItem); 
6221     if (iItem == -1) return -1;
6222
6223     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6224     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6225     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6226     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6227     lvItem.iItem = iItem;
6228     lvItem.iSubItem = 0;
6229     lvItem.pszText = szDispText;
6230     lvItem.cchTextMax = DISP_TEXT_SIZE;
6231     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6232     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6233
6234     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6235     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6236     opt.x = lpht->pt.x - Position.x - Origin.x;
6237     opt.y = lpht->pt.y - Position.y - Origin.y;
6238     
6239     if (uView == LVS_REPORT)
6240         rcBounds = rcBox;
6241     else
6242         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6243     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6244     if (!PtInRect(&rcBounds, opt)) return -1;
6245
6246     if (PtInRect(&rcIcon, opt))
6247         lpht->flags |= LVHT_ONITEMICON;
6248     else if (PtInRect(&rcLabel, opt))
6249         lpht->flags |= LVHT_ONITEMLABEL;
6250     else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6251         lpht->flags |= LVHT_ONITEMSTATEICON;
6252     if (lpht->flags & LVHT_ONITEM)
6253         lpht->flags &= ~LVHT_NOWHERE;
6254    
6255     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6256     if (uView == LVS_REPORT && subitem)
6257     {
6258         INT j;
6259
6260         rcBounds.right = rcBounds.left;
6261         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6262         {
6263             rcBounds.left = rcBounds.right;
6264             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6265             if (PtInRect(&rcBounds, opt))
6266             {
6267                 lpht->iSubItem = j;
6268                 break;
6269             }
6270         }
6271     }
6272
6273     if (select && !(uView == LVS_REPORT &&
6274                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6275                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6276     {
6277         if (uView == LVS_REPORT)
6278         {
6279             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6280             UnionRect(&rcBounds, &rcBounds, &rcState);
6281         }
6282         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6283     }
6284     return lpht->iItem = iItem;
6285 }
6286
6287
6288 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6289    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6290    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6291    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6292    their own sort proc. when sending LVM_SORTITEMS.
6293 */
6294 /* Platform SDK:
6295     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6296         if:
6297           LVS_SORTXXX must be specified,
6298           LVS_OWNERDRAW is not set,
6299           <item>.pszText is not LPSTR_TEXTCALLBACK.
6300
6301     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6302     are sorted based on item text..."
6303 */
6304 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6305 {
6306     ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6307     ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6308     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
6309
6310     /* if we're sorting descending, negate the return value */
6311     return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6312 }
6313
6314 /***
6315  * DESCRIPTION:
6316  * Inserts a new item in the listview control.
6317  *
6318  * PARAMETER(S):
6319  * [I] infoPtr : valid pointer to the listview structure
6320  * [I] lpLVItem : item information
6321  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6322  *
6323  * RETURN:
6324  *   SUCCESS : new item index
6325  *   FAILURE : -1
6326  */
6327 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6328 {
6329     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6330     INT nItem;
6331     HDPA hdpaSubItems;
6332     NMLISTVIEW nmlv;
6333     ITEM_INFO *lpItem;
6334     BOOL is_sorted, has_changed;
6335     LVITEMW item;
6336     HWND hwndSelf = infoPtr->hwndSelf;
6337
6338     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6339
6340     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6341
6342     /* make sure it's an item, and not a subitem; cannot insert a subitem */
6343     if (!lpLVItem || lpLVItem->iSubItem) return -1;
6344
6345     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6346
6347     if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6348     
6349     /* insert item in listview control data structure */
6350     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6351     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6352
6353     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6354                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6355
6356     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6357
6358     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6359     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6360     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6361     if (nItem == -1) goto fail;
6362     infoPtr->nItemCount++;
6363
6364     /* shift indices first so they don't get tangled */
6365     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6366
6367     /* set the item attributes */
6368     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6369     {
6370         /* full size structure expected - _WIN32IE >= 0x560 */
6371         item = *lpLVItem;
6372     }
6373     else if (lpLVItem->mask & LVIF_INDENT)
6374     {
6375         /* indent member expected - _WIN32IE >= 0x300 */
6376         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6377     }
6378     else
6379     {
6380         /* minimal structure expected */
6381         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6382     }
6383     item.iItem = nItem;
6384     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6385     {
6386         item.mask |= LVIF_STATE;
6387         item.stateMask |= LVIS_STATEIMAGEMASK;
6388         item.state &= ~LVIS_STATEIMAGEMASK;
6389         item.state |= INDEXTOSTATEIMAGEMASK(1);
6390     }
6391     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6392
6393     /* if we're sorted, sort the list, and update the index */
6394     if (is_sorted)
6395     {
6396         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6397         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6398         assert(nItem != -1);
6399     }
6400
6401     /* make room for the position, if we are in the right mode */
6402     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6403     {
6404         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6405             goto undo;
6406         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6407         {
6408             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6409             goto undo;
6410         }
6411     }
6412     
6413     /* send LVN_INSERTITEM notification */
6414     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6415     nmlv.iItem = nItem;
6416     nmlv.lParam = lpItem->lParam;
6417     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6418     if (!IsWindow(hwndSelf))
6419         return -1;
6420
6421     /* align items (set position of each item) */
6422     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6423     {
6424         POINT pt;
6425
6426         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6427             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6428         else
6429             LISTVIEW_NextIconPosTop(infoPtr, &pt);
6430
6431         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6432     }
6433
6434     /* now is the invalidation fun */
6435     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6436     return nItem;
6437
6438 undo:
6439     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6440     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6441     infoPtr->nItemCount--;
6442 fail:
6443     DPA_DeletePtr(hdpaSubItems, 0);
6444     DPA_Destroy (hdpaSubItems);
6445     Free (lpItem);
6446     return -1;
6447 }
6448
6449 /***
6450  * DESCRIPTION:
6451  * Redraws a range of items.
6452  *
6453  * PARAMETER(S):
6454  * [I] infoPtr : valid pointer to the listview structure
6455  * [I] nFirst : first item
6456  * [I] nLast : last item
6457  *
6458  * RETURN:
6459  *   SUCCESS : TRUE
6460  *   FAILURE : FALSE
6461  */
6462 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6463 {
6464     INT i;
6465  
6466     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
6467         max(nFirst, nLast) >= infoPtr->nItemCount)
6468         return FALSE;
6469     
6470     for (i = nFirst; i <= nLast; i++)
6471         LISTVIEW_InvalidateItem(infoPtr, i);
6472
6473     return TRUE;
6474 }
6475
6476 /***
6477  * DESCRIPTION:
6478  * Scroll the content of a listview.
6479  *
6480  * PARAMETER(S):
6481  * [I] infoPtr : valid pointer to the listview structure
6482  * [I] dx : horizontal scroll amount in pixels
6483  * [I] dy : vertical scroll amount in pixels
6484  *
6485  * RETURN:
6486  *   SUCCESS : TRUE
6487  *   FAILURE : FALSE
6488  *
6489  * COMMENTS:
6490  *  If the control is in report mode (LVS_REPORT) the control can
6491  *  be scrolled only in line increments. "dy" will be rounded to the
6492  *  nearest number of pixels that are a whole line. Ex: if line height
6493  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6494  *  is passed the the scroll will be 0.  (per MSDN 7/2002)
6495  *
6496  *  For:  (per experimentaion with native control and CSpy ListView)
6497  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
6498  *                    dx ignored
6499  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
6500  *                    dx ignored
6501  *     LVS_LIST       dx=1 = 1 column (horizontal only)
6502  *                           but will only scroll 1 column per message
6503  *                           no matter what the value.
6504  *                    dy must be 0 or FALSE returned.
6505  *     LVS_REPORT     dx=1 = 1 pixel
6506  *                    dy=  see above
6507  *
6508  */
6509 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6510 {
6511     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6512     case LVS_REPORT:
6513         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6514         dy /= infoPtr->nItemHeight;
6515         break;
6516     case LVS_LIST:
6517         if (dy != 0) return FALSE;
6518         break;
6519     default: /* icon */
6520         dx = 0;
6521         break;
6522     }   
6523
6524     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6525     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6526   
6527     return TRUE;
6528 }
6529
6530 /***
6531  * DESCRIPTION:
6532  * Sets the background color.
6533  *
6534  * PARAMETER(S):
6535  * [I] infoPtr : valid pointer to the listview structure
6536  * [I] clrBk : background color
6537  *
6538  * RETURN:
6539  *   SUCCESS : TRUE
6540  *   FAILURE : FALSE
6541  */
6542 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6543 {
6544     TRACE("(clrBk=%x)\n", clrBk);
6545
6546     if(infoPtr->clrBk != clrBk) {
6547         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6548         infoPtr->clrBk = clrBk;
6549         if (clrBk == CLR_NONE)
6550             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6551         else
6552             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6553         LISTVIEW_InvalidateList(infoPtr);
6554     }
6555
6556    return TRUE;
6557 }
6558
6559 /* LISTVIEW_SetBkImage */
6560
6561 /*** Helper for {Insert,Set}ColumnT *only* */
6562 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6563                                const LVCOLUMNW *lpColumn, BOOL isW)
6564 {
6565     if (lpColumn->mask & LVCF_FMT)
6566     {
6567         /* format member is valid */
6568         lphdi->mask |= HDI_FORMAT;
6569
6570         /* set text alignment (leftmost column must be left-aligned) */
6571         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6572             lphdi->fmt |= HDF_LEFT;
6573         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6574             lphdi->fmt |= HDF_RIGHT;
6575         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6576             lphdi->fmt |= HDF_CENTER;
6577
6578         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6579             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6580
6581         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6582         {
6583             lphdi->fmt |= HDF_IMAGE;
6584             lphdi->iImage = I_IMAGECALLBACK;
6585         }
6586     }
6587
6588     if (lpColumn->mask & LVCF_WIDTH)
6589     {
6590         lphdi->mask |= HDI_WIDTH;
6591         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6592         {
6593             /* make it fill the remainder of the controls width */
6594             RECT rcHeader;
6595             INT item_index;
6596
6597             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6598             {
6599                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6600                 lphdi->cxy += rcHeader.right - rcHeader.left;
6601             }
6602
6603             /* retrieve the layout of the header */
6604             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6605             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6606
6607             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6608         }
6609         else
6610             lphdi->cxy = lpColumn->cx;
6611     }
6612
6613     if (lpColumn->mask & LVCF_TEXT)
6614     {
6615         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6616         lphdi->fmt |= HDF_STRING;
6617         lphdi->pszText = lpColumn->pszText;
6618         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6619     }
6620
6621     if (lpColumn->mask & LVCF_IMAGE)
6622     {
6623         lphdi->mask |= HDI_IMAGE;
6624         lphdi->iImage = lpColumn->iImage;
6625     }
6626
6627     if (lpColumn->mask & LVCF_ORDER)
6628     {
6629         lphdi->mask |= HDI_ORDER;
6630         lphdi->iOrder = lpColumn->iOrder;
6631     }
6632 }
6633
6634
6635 /***
6636  * DESCRIPTION:
6637  * Inserts a new column.
6638  *
6639  * PARAMETER(S):
6640  * [I] infoPtr : valid pointer to the listview structure
6641  * [I] nColumn : column index
6642  * [I] lpColumn : column information
6643  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6644  *
6645  * RETURN:
6646  *   SUCCESS : new column index
6647  *   FAILURE : -1
6648  */
6649 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6650                                   const LVCOLUMNW *lpColumn, BOOL isW)
6651 {
6652     COLUMN_INFO *lpColumnInfo;
6653     INT nNewColumn;
6654     HDITEMW hdi;
6655
6656     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6657
6658     if (!lpColumn || nColumn < 0) return -1;
6659     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6660     
6661     ZeroMemory(&hdi, sizeof(HDITEMW));
6662     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6663
6664     /*
6665      * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6666      * (can be seen in SPY) otherwise column never gets added.
6667      */
6668     if (!(lpColumn->mask & LVCF_WIDTH)) {
6669         hdi.mask |= HDI_WIDTH;
6670         hdi.cxy = 10;
6671     }
6672
6673     /*
6674      * when the iSubItem is available Windows copies it to the header lParam. It seems
6675      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6676      */
6677     if (lpColumn->mask & LVCF_SUBITEM)
6678     {
6679         hdi.mask |= HDI_LPARAM;
6680         hdi.lParam = lpColumn->iSubItem;
6681     }
6682
6683     /* insert item in header control */
6684     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6685                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6686                               (WPARAM)nColumn, (LPARAM)&hdi);
6687     if (nNewColumn == -1) return -1;
6688     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6689    
6690     /* create our own column info */ 
6691     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6692     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6693
6694     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6695     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6696
6697     /* now we have to actually adjust the data */
6698     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6699     {
6700         SUBITEM_INFO *lpSubItem;
6701         HDPA hdpaSubItems;
6702         INT nItem, i;
6703         
6704         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6705         {
6706             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6707             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6708             {
6709                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6710                 if (lpSubItem->iSubItem >= nNewColumn)
6711                     lpSubItem->iSubItem++;
6712             }
6713         }
6714     }
6715
6716     /* make space for the new column */
6717     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6718     LISTVIEW_UpdateItemSize(infoPtr);
6719     
6720     return nNewColumn;
6721
6722 fail:
6723     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6724     if (lpColumnInfo)
6725     {
6726         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6727         Free(lpColumnInfo);
6728     }
6729     return -1;
6730 }
6731
6732 /***
6733  * DESCRIPTION:
6734  * Sets the attributes of a header item.
6735  *
6736  * PARAMETER(S):
6737  * [I] infoPtr : valid pointer to the listview structure
6738  * [I] nColumn : column index
6739  * [I] lpColumn : column attributes
6740  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6741  *
6742  * RETURN:
6743  *   SUCCESS : TRUE
6744  *   FAILURE : FALSE
6745  */
6746 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6747                                 const LVCOLUMNW *lpColumn, BOOL isW)
6748 {
6749     HDITEMW hdi, hdiget;
6750     BOOL bResult;
6751
6752     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6753     
6754     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6755
6756     ZeroMemory(&hdi, sizeof(HDITEMW));
6757     if (lpColumn->mask & LVCF_FMT)
6758     {
6759         hdi.mask |= HDI_FORMAT;
6760         hdiget.mask = HDI_FORMAT;
6761         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6762             hdi.fmt = hdiget.fmt & HDF_STRING;
6763     }
6764     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6765
6766     /* set header item attributes */
6767     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6768     if (!bResult) return FALSE;
6769
6770     if (lpColumn->mask & LVCF_FMT)
6771     {
6772         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6773         int oldFmt = lpColumnInfo->fmt;
6774         
6775         lpColumnInfo->fmt = lpColumn->fmt;
6776         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6777         {
6778             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6779             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6780         }
6781     }
6782
6783     return TRUE;
6784 }
6785
6786 /***
6787  * DESCRIPTION:
6788  * Sets the column order array
6789  *
6790  * PARAMETERS:
6791  * [I] infoPtr : valid pointer to the listview structure
6792  * [I] iCount : number of elements in column order array
6793  * [I] lpiArray : pointer to column order array
6794  *
6795  * RETURN:
6796  *   SUCCESS : TRUE
6797  *   FAILURE : FALSE
6798  */
6799 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6800 {
6801   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6802
6803   if (!lpiArray)
6804     return FALSE;
6805
6806   return TRUE;
6807 }
6808
6809
6810 /***
6811  * DESCRIPTION:
6812  * Sets the width of a column
6813  *
6814  * PARAMETERS:
6815  * [I] infoPtr : valid pointer to the listview structure
6816  * [I] nColumn : column index
6817  * [I] cx : column width
6818  *
6819  * RETURN:
6820  *   SUCCESS : TRUE
6821  *   FAILURE : FALSE
6822  */
6823 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6824 {
6825     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6826     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6827     INT max_cx = 0;
6828     HDITEMW hdi;
6829
6830     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6831
6832     /* set column width only if in report or list mode */
6833     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6834
6835     /* take care of invalid cx values */
6836     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6837     else if (uView == LVS_LIST && cx < 1) return FALSE;
6838
6839     /* resize all columns if in LVS_LIST mode */
6840     if(uView == LVS_LIST) 
6841     {
6842         infoPtr->nItemWidth = cx;
6843         LISTVIEW_InvalidateList(infoPtr);
6844         return TRUE;
6845     }
6846
6847     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6848
6849     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6850     {
6851         INT nLabelWidth;
6852         LVITEMW lvItem;
6853
6854         lvItem.mask = LVIF_TEXT;        
6855         lvItem.iItem = 0;
6856         lvItem.iSubItem = nColumn;
6857         lvItem.pszText = szDispText;
6858         lvItem.cchTextMax = DISP_TEXT_SIZE;
6859         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6860         {
6861             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6862             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6863             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6864         }
6865         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6866             max_cx += infoPtr->iconSize.cx;
6867         max_cx += TRAILING_LABEL_PADDING;
6868     }
6869
6870     /* autosize based on listview items width */
6871     if(cx == LVSCW_AUTOSIZE)
6872         cx = max_cx;
6873     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6874     {
6875         /* if iCol is the last column make it fill the remainder of the controls width */
6876         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
6877         {
6878             RECT rcHeader;
6879             POINT Origin;
6880
6881             LISTVIEW_GetOrigin(infoPtr, &Origin);
6882             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6883
6884             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6885         }
6886         else
6887         {
6888             /* Despite what the MS docs say, if this is not the last
6889                column, then MS resizes the column to the width of the
6890                largest text string in the column, including headers
6891                and items. This is different from LVSCW_AUTOSIZE in that
6892                LVSCW_AUTOSIZE ignores the header string length. */
6893             cx = 0;
6894
6895             /* retrieve header text */
6896             hdi.mask = HDI_TEXT;
6897             hdi.cchTextMax = DISP_TEXT_SIZE;
6898             hdi.pszText = szDispText;
6899             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6900             {
6901                 HDC hdc = GetDC(infoPtr->hwndSelf);
6902                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6903                 SIZE size;
6904
6905                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6906                     cx = size.cx + TRAILING_HEADER_PADDING;
6907                 /* FIXME: Take into account the header image, if one is present */
6908                 SelectObject(hdc, old_font);
6909                 ReleaseDC(infoPtr->hwndSelf, hdc);
6910             }
6911             cx = max (cx, max_cx);
6912         }
6913     }
6914
6915     if (cx < 0) return FALSE;
6916
6917     /* call header to update the column change */
6918     hdi.mask = HDI_WIDTH;
6919     hdi.cxy = cx;
6920     TRACE("hdi.cxy=%d\n", hdi.cxy);
6921     return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6922 }
6923
6924 /***
6925  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
6926  *
6927  */
6928 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
6929 {
6930     HDC hdc_wnd, hdc;
6931     HBITMAP hbm_im, hbm_mask, hbm_orig;
6932     RECT rc;
6933     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6934     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6935     HIMAGELIST himl;
6936
6937     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6938                             ILC_COLOR | ILC_MASK, 2, 2);
6939     hdc_wnd = GetDC(infoPtr->hwndSelf);
6940     hdc = CreateCompatibleDC(hdc_wnd);
6941     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6942     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6943     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6944
6945     rc.left = rc.top = 0;
6946     rc.right = GetSystemMetrics(SM_CXSMICON);
6947     rc.bottom = GetSystemMetrics(SM_CYSMICON);
6948
6949     hbm_orig = SelectObject(hdc, hbm_mask);
6950     FillRect(hdc, &rc, hbr_white);
6951     InflateRect(&rc, -3, -3);
6952     FillRect(hdc, &rc, hbr_black);
6953
6954     SelectObject(hdc, hbm_im);
6955     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6956     SelectObject(hdc, hbm_orig);
6957     ImageList_Add(himl, hbm_im, hbm_mask); 
6958
6959     SelectObject(hdc, hbm_im);
6960     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6961     SelectObject(hdc, hbm_orig);
6962     ImageList_Add(himl, hbm_im, hbm_mask);
6963
6964     DeleteObject(hbm_mask);
6965     DeleteObject(hbm_im);
6966     DeleteDC(hdc);
6967
6968     return himl;
6969 }
6970
6971 /***
6972  * DESCRIPTION:
6973  * Sets the extended listview style.
6974  *
6975  * PARAMETERS:
6976  * [I] infoPtr : valid pointer to the listview structure
6977  * [I] dwMask : mask
6978  * [I] dwStyle : style
6979  *
6980  * RETURN:
6981  *   SUCCESS : previous style
6982  *   FAILURE : 0
6983  */
6984 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6985 {
6986     DWORD dwOldStyle = infoPtr->dwLvExStyle;
6987
6988     /* set new style */
6989     if (dwMask)
6990         infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6991     else
6992         infoPtr->dwLvExStyle = dwStyle;
6993
6994     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6995     {
6996         HIMAGELIST himl = 0;
6997         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6998         {
6999             LVITEMW item;
7000             item.mask = LVIF_STATE;
7001             item.stateMask = LVIS_STATEIMAGEMASK;
7002             item.state = INDEXTOSTATEIMAGEMASK(1);
7003             LISTVIEW_SetItemState(infoPtr, -1, &item);
7004
7005             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7006         }
7007         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7008     }
7009     
7010     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_HEADERDRAGDROP)
7011     {
7012         DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7013         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7014             dwStyle |= HDS_DRAGDROP;
7015         else
7016             dwStyle &= ~HDS_DRAGDROP;
7017         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7018     }
7019
7020     return dwOldStyle;
7021 }
7022
7023 /***
7024  * DESCRIPTION:
7025  * Sets the new hot cursor used during hot tracking and hover selection.
7026  *
7027  * PARAMETER(S):
7028  * [I] infoPtr : valid pointer to the listview structure
7029  * [I] hCursor : the new hot cursor handle
7030  *
7031  * RETURN:
7032  * Returns the previous hot cursor
7033  */
7034 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7035 {
7036     HCURSOR oldCursor = infoPtr->hHotCursor;
7037     
7038     infoPtr->hHotCursor = hCursor;
7039
7040     return oldCursor;
7041 }
7042
7043
7044 /***
7045  * DESCRIPTION:
7046  * Sets the hot item index.
7047  *
7048  * PARAMETERS:
7049  * [I] infoPtr : valid pointer to the listview structure
7050  * [I] iIndex : index
7051  *
7052  * RETURN:
7053  *   SUCCESS : previous hot item index
7054  *   FAILURE : -1 (no hot item)
7055  */
7056 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7057 {
7058     INT iOldIndex = infoPtr->nHotItem;
7059     
7060     infoPtr->nHotItem = iIndex;
7061     
7062     return iOldIndex;
7063 }
7064
7065
7066 /***
7067  * DESCRIPTION:
7068  * Sets the amount of time the cursor must hover over an item before it is selected.
7069  *
7070  * PARAMETER(S):
7071  * [I] infoPtr : valid pointer to the listview structure
7072  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7073  *
7074  * RETURN:
7075  * Returns the previous hover time
7076  */
7077 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7078 {
7079     DWORD oldHoverTime = infoPtr->dwHoverTime;
7080     
7081     infoPtr->dwHoverTime = dwHoverTime;
7082     
7083     return oldHoverTime;
7084 }
7085
7086 /***
7087  * DESCRIPTION:
7088  * Sets spacing for icons of LVS_ICON style.
7089  *
7090  * PARAMETER(S):
7091  * [I] infoPtr : valid pointer to the listview structure
7092  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7093  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7094  *
7095  * RETURN:
7096  *   MAKELONG(oldcx, oldcy)
7097  */
7098 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7099 {
7100     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7101     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7102
7103     TRACE("requested=(%d,%d)\n", cx, cy);
7104     
7105     /* this is supported only for LVS_ICON style */
7106     if (uView != LVS_ICON) return oldspacing;
7107   
7108     /* set to defaults, if instructed to */
7109     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7110     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7111
7112     /* if 0 then compute width
7113      * FIXME: Should scan each item and determine max width of
7114      *        icon or label, then make that the width */
7115     if (cx == 0)
7116         cx = infoPtr->iconSpacing.cx;
7117
7118     /* if 0 then compute height */
7119     if (cy == 0) 
7120         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7121              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7122     
7123
7124     infoPtr->iconSpacing.cx = cx;
7125     infoPtr->iconSpacing.cy = cy;
7126
7127     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7128           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
7129           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7130           infoPtr->ntmHeight);
7131
7132     /* these depend on the iconSpacing */
7133     LISTVIEW_UpdateItemSize(infoPtr);
7134
7135     return oldspacing;
7136 }
7137
7138 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7139 {
7140     INT cx, cy;
7141     
7142     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7143     {
7144         size->cx = cx;
7145         size->cy = cy;
7146     }
7147     else
7148     {
7149         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7150         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7151     }
7152 }
7153
7154 /***
7155  * DESCRIPTION:
7156  * Sets image lists.
7157  *
7158  * PARAMETER(S):
7159  * [I] infoPtr : valid pointer to the listview structure
7160  * [I] nType : image list type
7161  * [I] himl : image list handle
7162  *
7163  * RETURN:
7164  *   SUCCESS : old image list
7165  *   FAILURE : NULL
7166  */
7167 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7168 {
7169     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7170     INT oldHeight = infoPtr->nItemHeight;
7171     HIMAGELIST himlOld = 0;
7172
7173     TRACE("(nType=%d, himl=%p\n", nType, himl);
7174
7175     switch (nType)
7176     {
7177     case LVSIL_NORMAL:
7178         himlOld = infoPtr->himlNormal;
7179         infoPtr->himlNormal = himl;
7180         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7181         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7182     break;
7183
7184     case LVSIL_SMALL:
7185         himlOld = infoPtr->himlSmall;
7186         infoPtr->himlSmall = himl;
7187          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7188     break;
7189
7190     case LVSIL_STATE:
7191         himlOld = infoPtr->himlState;
7192         infoPtr->himlState = himl;
7193         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7194         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7195     break;
7196
7197     default:
7198         ERR("Unknown icon type=%d\n", nType);
7199         return NULL;
7200     }
7201
7202     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7203     if (infoPtr->nItemHeight != oldHeight)
7204         LISTVIEW_UpdateScroll(infoPtr);
7205
7206     return himlOld;
7207 }
7208
7209 /***
7210  * DESCRIPTION:
7211  * Preallocates memory (does *not* set the actual count of items !)
7212  *
7213  * PARAMETER(S):
7214  * [I] infoPtr : valid pointer to the listview structure
7215  * [I] nItems : item count (projected number of items to allocate)
7216  * [I] dwFlags : update flags
7217  *
7218  * RETURN:
7219  *   SUCCESS : TRUE
7220  *   FAILURE : FALSE
7221  */
7222 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7223 {
7224     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7225
7226     if (infoPtr->dwStyle & LVS_OWNERDATA)
7227     {
7228         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7229         INT nOldCount = infoPtr->nItemCount;
7230
7231         if (nItems < nOldCount)
7232         {
7233             RANGE range = { nItems, nOldCount };
7234             ranges_del(infoPtr->selectionRanges, range);
7235             if (infoPtr->nFocusedItem >= nItems)
7236             {
7237                 infoPtr->nFocusedItem = -1;
7238                 SetRectEmpty(&infoPtr->rcFocus);
7239             }
7240         }
7241
7242         infoPtr->nItemCount = nItems;
7243         LISTVIEW_UpdateScroll(infoPtr);
7244
7245         /* the flags are valid only in ownerdata report and list modes */
7246         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7247
7248         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7249             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7250
7251         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7252             LISTVIEW_InvalidateList(infoPtr);
7253         else
7254         {
7255             INT nFrom, nTo;
7256             POINT Origin;
7257             RECT rcErase;
7258             
7259             LISTVIEW_GetOrigin(infoPtr, &Origin);
7260             nFrom = min(nOldCount, nItems);
7261             nTo = max(nOldCount, nItems);
7262     
7263             if (uView == LVS_REPORT)
7264             {
7265                 rcErase.left = 0;
7266                 rcErase.top = nFrom * infoPtr->nItemHeight;
7267                 rcErase.right = infoPtr->nItemWidth;
7268                 rcErase.bottom = nTo * infoPtr->nItemHeight;
7269                 OffsetRect(&rcErase, Origin.x, Origin.y);
7270                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7271                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7272             }
7273             else /* LVS_LIST */
7274             {
7275                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7276
7277                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7278                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7279                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7280                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7281                 OffsetRect(&rcErase, Origin.x, Origin.y);
7282                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7283                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7284
7285                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7286                 rcErase.top = 0;
7287                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7288                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7289                 OffsetRect(&rcErase, Origin.x, Origin.y);
7290                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7291                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7292             }
7293         }
7294     }
7295     else
7296     {
7297         /* According to MSDN for non-LVS_OWNERDATA this is just
7298          * a performance issue. The control allocates its internal
7299          * data structures for the number of items specified. It
7300          * cuts down on the number of memory allocations. Therefore
7301          * we will just issue a WARN here
7302          */
7303         WARN("for non-ownerdata performance option not implemented.\n");
7304     }
7305
7306     return TRUE;
7307 }
7308
7309 /***
7310  * DESCRIPTION:
7311  * Sets the position of an item.
7312  *
7313  * PARAMETER(S):
7314  * [I] infoPtr : valid pointer to the listview structure
7315  * [I] nItem : item index
7316  * [I] pt : coordinate
7317  *
7318  * RETURN:
7319  *   SUCCESS : TRUE
7320  *   FAILURE : FALSE
7321  */
7322 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7323 {
7324     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7325     POINT Origin;
7326
7327     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7328
7329     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7330         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7331
7332     LISTVIEW_GetOrigin(infoPtr, &Origin);
7333
7334     /* This point value seems to be an undocumented feature.
7335      * The best guess is that it means either at the origin, 
7336      * or at true beginning of the list. I will assume the origin. */
7337     if ((pt.x == -1) && (pt.y == -1))
7338         pt = Origin;
7339     
7340     if (uView == LVS_ICON)
7341     {
7342         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7343         pt.y -= ICON_TOP_PADDING;
7344     }
7345     pt.x -= Origin.x;
7346     pt.y -= Origin.y;
7347
7348     infoPtr->bAutoarrange = FALSE;
7349
7350     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7351 }
7352
7353 /***
7354  * DESCRIPTION:
7355  * Sets the state of one or many items.
7356  *
7357  * PARAMETER(S):
7358  * [I] infoPtr : valid pointer to the listview structure
7359  * [I] nItem : item index
7360  * [I] lpLVItem : item or subitem info
7361  *
7362  * RETURN:
7363  *   SUCCESS : TRUE
7364  *   FAILURE : FALSE
7365  */
7366 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7367 {
7368     BOOL bResult = TRUE;
7369     LVITEMW lvItem;
7370
7371     lvItem.iItem = nItem;
7372     lvItem.iSubItem = 0;
7373     lvItem.mask = LVIF_STATE;
7374     lvItem.state = lpLVItem->state;
7375     lvItem.stateMask = lpLVItem->stateMask;
7376     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7377
7378     if (nItem == -1)
7379     {
7380         /* apply to all items */
7381         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7382             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7383     }
7384     else
7385         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7386
7387     /*
7388      * Update selection mark
7389      *
7390      * Investigation on windows 2k showed that selection mark was updated
7391      * whenever a new selection was made, but if the selected item was
7392      * unselected it was not updated.
7393      *
7394      * we are probably still not 100% accurate, but this at least sets the
7395      * proper selection mark when it is needed
7396      */
7397
7398     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7399         ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7400     {
7401         int i;
7402         infoPtr->nSelectionMark = -1;
7403         for (i = 0; i < infoPtr->nItemCount; i++)
7404         {
7405             if (infoPtr->uCallbackMask & LVIS_SELECTED)
7406             {
7407                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7408                 {
7409                     infoPtr->nSelectionMark = i;
7410                     break;
7411                 }
7412             }
7413             else if (ranges_contain(infoPtr->selectionRanges, i))
7414             {
7415                 infoPtr->nSelectionMark = i;
7416                 break;
7417             }
7418         }
7419     }
7420
7421     return bResult;
7422 }
7423
7424 /***
7425  * DESCRIPTION:
7426  * Sets the text of an item or subitem.
7427  *
7428  * PARAMETER(S):
7429  * [I] hwnd : window handle
7430  * [I] nItem : item index
7431  * [I] lpLVItem : item or subitem info
7432  * [I] isW : TRUE if input is Unicode
7433  *
7434  * RETURN:
7435  *   SUCCESS : TRUE
7436  *   FAILURE : FALSE
7437  */
7438 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7439 {
7440     LVITEMW lvItem;
7441
7442     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7443     
7444     lvItem.iItem = nItem;
7445     lvItem.iSubItem = lpLVItem->iSubItem;
7446     lvItem.mask = LVIF_TEXT;
7447     lvItem.pszText = lpLVItem->pszText;
7448     lvItem.cchTextMax = lpLVItem->cchTextMax;
7449     
7450     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7451
7452     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
7453 }
7454
7455 /***
7456  * DESCRIPTION:
7457  * Set item index that marks the start of a multiple selection.
7458  *
7459  * PARAMETER(S):
7460  * [I] infoPtr : valid pointer to the listview structure
7461  * [I] nIndex : index
7462  *
7463  * RETURN:
7464  * Index number or -1 if there is no selection mark.
7465  */
7466 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7467 {
7468   INT nOldIndex = infoPtr->nSelectionMark;
7469
7470   TRACE("(nIndex=%d)\n", nIndex);
7471
7472   infoPtr->nSelectionMark = nIndex;
7473
7474   return nOldIndex;
7475 }
7476
7477 /***
7478  * DESCRIPTION:
7479  * Sets the text background color.
7480  *
7481  * PARAMETER(S):
7482  * [I] infoPtr : valid pointer to the listview structure
7483  * [I] clrTextBk : text background color
7484  *
7485  * RETURN:
7486  *   SUCCESS : TRUE
7487  *   FAILURE : FALSE
7488  */
7489 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7490 {
7491     TRACE("(clrTextBk=%x)\n", clrTextBk);
7492
7493     if (infoPtr->clrTextBk != clrTextBk)
7494     {
7495         infoPtr->clrTextBk = clrTextBk;
7496         LISTVIEW_InvalidateList(infoPtr);
7497     }
7498     
7499   return TRUE;
7500 }
7501
7502 /***
7503  * DESCRIPTION:
7504  * Sets the text foreground color.
7505  *
7506  * PARAMETER(S):
7507  * [I] infoPtr : valid pointer to the listview structure
7508  * [I] clrText : text color
7509  *
7510  * RETURN:
7511  *   SUCCESS : TRUE
7512  *   FAILURE : FALSE
7513  */
7514 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7515 {
7516     TRACE("(clrText=%x)\n", clrText);
7517
7518     if (infoPtr->clrText != clrText)
7519     {
7520         infoPtr->clrText = clrText;
7521         LISTVIEW_InvalidateList(infoPtr);
7522     }
7523
7524     return TRUE;
7525 }
7526
7527 /***
7528  * DESCRIPTION:
7529  * Determines which listview item is located at the specified position.
7530  *
7531  * PARAMETER(S):
7532  * [I] infoPtr        : valid pointer to the listview structure
7533  * [I] hwndNewToolTip : handle to new ToolTip
7534  *
7535  * RETURN:
7536  *   old tool tip
7537  */
7538 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7539 {
7540   HWND hwndOldToolTip = infoPtr->hwndToolTip;
7541   infoPtr->hwndToolTip = hwndNewToolTip;
7542   return hwndOldToolTip;
7543 }
7544
7545 /*
7546  * DESCRIPTION:
7547  *   sets the Unicode character format flag for the control
7548  * PARAMETER(S):
7549  *    [I] infoPtr         :valid pointer to the listview structure
7550  *    [I] fUnicode        :true to switch to UNICODE false to switch to ANSI
7551  *
7552  * RETURN:
7553  *    Old Unicode Format
7554  */
7555 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7556 {
7557   BOOL rc = infoPtr->notifyFormat;
7558   infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7559   return rc;
7560 }
7561
7562 /* LISTVIEW_SetWorkAreas */
7563
7564 /***
7565  * DESCRIPTION:
7566  * Callback internally used by LISTVIEW_SortItems()
7567  *
7568  * PARAMETER(S):
7569  * [I] first : pointer to first ITEM_INFO to compare
7570  * [I] second : pointer to second ITEM_INFO to compare
7571  * [I] lParam : HWND of control
7572  *
7573  * RETURN:
7574  *   if first comes before second : negative
7575  *   if first comes after second : positive
7576  *   if first and second are equivalent : zero
7577  */
7578 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7579 {
7580   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7581   ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7582   ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7583
7584   /* Forward the call to the client defined callback */
7585   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7586 }
7587
7588 /***
7589  * DESCRIPTION:
7590  * Sorts the listview items.
7591  *
7592  * PARAMETER(S):
7593  * [I] infoPtr : valid pointer to the listview structure
7594  * [I] pfnCompare : application-defined value
7595  * [I] lParamSort : pointer to comparision callback
7596  *
7597  * RETURN:
7598  *   SUCCESS : TRUE
7599  *   FAILURE : FALSE
7600  */
7601 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7602 {
7603     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7604     HDPA hdpaSubItems;
7605     ITEM_INFO *lpItem;
7606     LPVOID selectionMarkItem;
7607     LVITEMW item;
7608     int i;
7609
7610     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7611
7612     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7613
7614     if (!pfnCompare) return FALSE;
7615     if (!infoPtr->hdpaItems) return FALSE;
7616
7617     /* if there are 0 or 1 items, there is no need to sort */
7618     if (infoPtr->nItemCount < 2) return TRUE;
7619
7620     if (infoPtr->nFocusedItem >= 0)
7621     {
7622         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7623         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7624         if (lpItem) lpItem->state |= LVIS_FOCUSED;
7625     }
7626     /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7627     /*        clear the lpItem->state for non-selected ones */
7628     /*        remove the selection ranges */
7629     
7630     infoPtr->pfnCompare = pfnCompare;
7631     infoPtr->lParamSort = lParamSort;
7632     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7633
7634     /* Adjust selections and indices so that they are the way they should
7635      * be after the sort (otherwise, the list items move around, but
7636      * whatever is at the item's previous original position will be
7637      * selected instead)
7638      */
7639     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7640     for (i=0; i < infoPtr->nItemCount; i++)
7641     {
7642         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7643         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7644
7645         if (lpItem->state & LVIS_SELECTED)
7646         {
7647             item.state = LVIS_SELECTED;
7648             item.stateMask = LVIS_SELECTED;
7649             LISTVIEW_SetItemState(infoPtr, i, &item);
7650         }
7651         if (lpItem->state & LVIS_FOCUSED)
7652         {
7653             infoPtr->nFocusedItem = i;
7654             lpItem->state &= ~LVIS_FOCUSED;
7655         }
7656     }
7657     if (selectionMarkItem != NULL)
7658         infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7659     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7660
7661     /* refresh the display */
7662     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7663         LISTVIEW_InvalidateList(infoPtr);
7664
7665     return TRUE;
7666 }
7667
7668 /***
7669  * DESCRIPTION:
7670  * Update theme handle after a theme change.
7671  *
7672  * PARAMETER(S):
7673  * [I] infoPtr : valid pointer to the listview structure
7674  *
7675  * RETURN:
7676  *   SUCCESS : 0
7677  *   FAILURE : something else
7678  */
7679 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7680 {
7681     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7682     CloseThemeData(theme);
7683     OpenThemeData(infoPtr->hwndSelf, themeClass);
7684     return 0;
7685 }
7686
7687 /***
7688  * DESCRIPTION:
7689  * Updates an items or rearranges the listview control.
7690  *
7691  * PARAMETER(S):
7692  * [I] infoPtr : valid pointer to the listview structure
7693  * [I] nItem : item index
7694  *
7695  * RETURN:
7696  *   SUCCESS : TRUE
7697  *   FAILURE : FALSE
7698  */
7699 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7700 {
7701     TRACE("(nItem=%d)\n", nItem);
7702
7703     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7704
7705     /* rearrange with default alignment style */
7706     if (is_autoarrange(infoPtr))
7707         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7708     else
7709         LISTVIEW_InvalidateItem(infoPtr, nItem);
7710
7711     return TRUE;
7712 }
7713
7714 /***
7715  * DESCRIPTION:
7716  * Draw the track line at the place defined in the infoPtr structure.
7717  * The line is drawn with a XOR pen so drawing the line for the second time
7718  * in the same place erases the line.
7719  *
7720  * PARAMETER(S):
7721  * [I] infoPtr : valid pointer to the listview structure
7722  *
7723  * RETURN:
7724  *   SUCCESS : TRUE
7725  *   FAILURE : FALSE
7726  */
7727 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7728 {
7729     HPEN hOldPen;
7730     HDC hdc;
7731     INT oldROP;
7732
7733     if (infoPtr->xTrackLine == -1)
7734         return FALSE;
7735
7736     if (!(hdc = GetDC(infoPtr->hwndSelf)))
7737         return FALSE;
7738     hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7739     oldROP = SetROP2(hdc, R2_XORPEN);
7740     MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7741     LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7742     SetROP2(hdc, oldROP);
7743     SelectObject(hdc, hOldPen);
7744     ReleaseDC(infoPtr->hwndSelf, hdc);
7745     return TRUE;
7746 }
7747
7748 /***
7749  * DESCRIPTION:
7750  * Called when an edit control should be displayed. This function is called after
7751  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7752  *
7753  * PARAMETER(S):
7754  * [I] hwnd : Handle to the listview
7755  * [I] uMsg : WM_TIMER (ignored)
7756  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7757  * [I] dwTimer : The elapsed time (ignored)
7758  *
7759  * RETURN:
7760  *   None.
7761  */
7762 static CALLBACK VOID LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7763 {
7764     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7765     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7766
7767     KillTimer(hwnd, idEvent);
7768     editItem->fEnabled = FALSE;
7769     /* check if the item is still selected */
7770     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7771         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7772 }
7773
7774 /***
7775  * DESCRIPTION:
7776  * Creates the listview control - the WM_NCCREATE phase.
7777  *
7778  * PARAMETER(S):
7779  * [I] hwnd : window handle
7780  * [I] lpcs : the create parameters
7781  *
7782  * RETURN:
7783  *   Success: TRUE
7784  *   Failure: FALSE
7785  */
7786 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7787 {
7788   LISTVIEW_INFO *infoPtr;
7789   LOGFONTW logFont;
7790
7791   TRACE("(lpcs=%p)\n", lpcs);
7792
7793   /* initialize info pointer */
7794   infoPtr = Alloc(sizeof(LISTVIEW_INFO));
7795   if (!infoPtr) return FALSE;
7796
7797   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7798
7799   infoPtr->hwndSelf = hwnd;
7800   infoPtr->dwStyle = lpcs->style;    /* Note: may be changed in WM_CREATE */
7801   /* determine the type of structures to use */
7802   infoPtr->hwndNotify = lpcs->hwndParent;
7803   /* infoPtr->notifyFormat will be filled in WM_CREATE */
7804
7805   /* initialize color information  */
7806   infoPtr->clrBk = CLR_NONE;
7807   infoPtr->clrText = CLR_DEFAULT;
7808   infoPtr->clrTextBk = CLR_DEFAULT;
7809   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7810
7811   /* set default values */
7812   infoPtr->nFocusedItem = -1;
7813   infoPtr->nSelectionMark = -1;
7814   infoPtr->nHotItem = -1;
7815   infoPtr->bRedraw = TRUE;
7816   infoPtr->bNoItemMetrics = TRUE;
7817   infoPtr->bDoChangeNotify = TRUE;
7818   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7819   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7820   infoPtr->nEditLabelItem = -1;
7821   infoPtr->dwHoverTime = -1; /* default system hover time */
7822   infoPtr->nMeasureItemHeight = 0;
7823   infoPtr->xTrackLine = -1;  /* no track line */
7824   infoPtr->itemEdit.fEnabled = FALSE;
7825
7826   /* get default font (icon title) */
7827   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7828   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7829   infoPtr->hFont = infoPtr->hDefaultFont;
7830   LISTVIEW_SaveTextMetrics(infoPtr);
7831
7832   /* allocate memory for the data structure */
7833   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7834   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7835   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
7836   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
7837   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7838   return TRUE;
7839
7840 fail:
7841     DestroyWindow(infoPtr->hwndHeader);
7842     ranges_destroy(infoPtr->selectionRanges);
7843     DPA_Destroy(infoPtr->hdpaItems);
7844     DPA_Destroy(infoPtr->hdpaPosX);
7845     DPA_Destroy(infoPtr->hdpaPosY);
7846     DPA_Destroy(infoPtr->hdpaColumns);
7847     Free(infoPtr);
7848     return FALSE;
7849 }
7850
7851 /***
7852  * DESCRIPTION:
7853  * Creates the listview control - the WM_CREATE phase. Most of the data is
7854  * already set up in LISTVIEW_NCCreate
7855  *
7856  * PARAMETER(S):
7857  * [I] hwnd : window handle
7858  * [I] lpcs : the create parameters
7859  *
7860  * RETURN:
7861  *   Success: 0
7862  *   Failure: -1
7863  */
7864 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7865 {
7866   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7867   UINT uView = lpcs->style & LVS_TYPEMASK;
7868
7869   TRACE("(lpcs=%p)\n", lpcs);
7870
7871   infoPtr->dwStyle = lpcs->style;
7872   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7873                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7874
7875   /* create header */
7876   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7877     WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7878     0, 0, 0, 0, hwnd, NULL,
7879     lpcs->hInstance, NULL);
7880   if (!infoPtr->hwndHeader) return -1;
7881
7882   /* set header unicode format */
7883   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7884
7885   /* set header font */
7886   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7887
7888   /* init item size to avoid division by 0 */
7889   LISTVIEW_UpdateItemSize (infoPtr);
7890
7891   if (uView == LVS_REPORT)
7892   {
7893     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7894     {
7895       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7896     }
7897     else
7898     {
7899       /* set HDS_HIDDEN flag to hide the header bar */
7900       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7901                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7902     }
7903   }
7904
7905   OpenThemeData(hwnd, themeClass);
7906
7907   /* initialize the icon sizes */
7908   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7909   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7910   return 0;
7911 }
7912
7913 /***
7914  * DESCRIPTION:
7915  * Destroys the listview control.
7916  *
7917  * PARAMETER(S):
7918  * [I] infoPtr : valid pointer to the listview structure
7919  *
7920  * RETURN:
7921  *   Success: 0
7922  *   Failure: -1
7923  */
7924 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
7925 {
7926     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7927     CloseThemeData(theme);
7928     return 0;
7929 }
7930
7931 /***
7932  * DESCRIPTION:
7933  * Enables the listview control.
7934  *
7935  * PARAMETER(S):
7936  * [I] infoPtr : valid pointer to the listview structure
7937  * [I] bEnable : specifies whether to enable or disable the window
7938  *
7939  * RETURN:
7940  *   SUCCESS : TRUE
7941  *   FAILURE : FALSE
7942  */
7943 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
7944 {
7945     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7946         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7947     return TRUE;
7948 }
7949
7950 /***
7951  * DESCRIPTION:
7952  * Erases the background of the listview control.
7953  *
7954  * PARAMETER(S):
7955  * [I] infoPtr : valid pointer to the listview structure
7956  * [I] hdc : device context handle
7957  *
7958  * RETURN:
7959  *   SUCCESS : TRUE
7960  *   FAILURE : FALSE
7961  */
7962 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
7963 {
7964     RECT rc;
7965
7966     TRACE("(hdc=%p)\n", hdc);
7967
7968     if (!GetClipBox(hdc, &rc)) return FALSE;
7969
7970     /* for double buffered controls we need to do this during refresh */
7971     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
7972
7973     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7974 }
7975         
7976
7977 /***
7978  * DESCRIPTION:
7979  * Helper function for LISTVIEW_[HV]Scroll *only*.
7980  * Performs vertical/horizontal scrolling by a give amount.
7981  *
7982  * PARAMETER(S):
7983  * [I] infoPtr : valid pointer to the listview structure
7984  * [I] dx : amount of horizontal scroll
7985  * [I] dy : amount of vertical scroll
7986  */
7987 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7988 {
7989     /* now we can scroll the list */
7990     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
7991                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7992     /* if we have focus, adjust rect */
7993     OffsetRect(&infoPtr->rcFocus, dx, dy);
7994     UpdateWindow(infoPtr->hwndSelf);
7995 }
7996
7997 /***
7998  * DESCRIPTION:
7999  * Performs vertical scrolling.
8000  *
8001  * PARAMETER(S):
8002  * [I] infoPtr : valid pointer to the listview structure
8003  * [I] nScrollCode : scroll code
8004  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8005  * [I] hScrollWnd  : scrollbar control window handle
8006  *
8007  * RETURN:
8008  * Zero
8009  *
8010  * NOTES:
8011  *   SB_LINEUP/SB_LINEDOWN:
8012  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
8013  *        for LVS_REPORT is 1 line
8014  *        for LVS_LIST cannot occur
8015  *
8016  */
8017 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
8018                                 INT nScrollDiff, HWND hScrollWnd)
8019 {
8020     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8021     INT nOldScrollPos, nNewScrollPos;
8022     SCROLLINFO scrollInfo;
8023     BOOL is_an_icon;
8024
8025     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8026         debugscrollcode(nScrollCode), nScrollDiff);
8027
8028     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8029
8030     scrollInfo.cbSize = sizeof(SCROLLINFO);
8031     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8032
8033     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8034
8035     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8036
8037     nOldScrollPos = scrollInfo.nPos;
8038     switch (nScrollCode)
8039     {
8040     case SB_INTERNAL:
8041         break;
8042
8043     case SB_LINEUP:
8044         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8045         break;
8046
8047     case SB_LINEDOWN:
8048         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8049         break;
8050
8051     case SB_PAGEUP:
8052         nScrollDiff = -scrollInfo.nPage;
8053         break;
8054
8055     case SB_PAGEDOWN:
8056         nScrollDiff = scrollInfo.nPage;
8057         break;
8058
8059     case SB_THUMBPOSITION:
8060     case SB_THUMBTRACK:
8061         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8062         break;
8063
8064     default:
8065         nScrollDiff = 0;
8066     }
8067
8068     /* quit right away if pos isn't changing */
8069     if (nScrollDiff == 0) return 0;
8070     
8071     /* calculate new position, and handle overflows */
8072     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8073     if (nScrollDiff > 0) {
8074         if (nNewScrollPos < nOldScrollPos ||
8075             nNewScrollPos > scrollInfo.nMax)
8076             nNewScrollPos = scrollInfo.nMax;
8077     } else {
8078         if (nNewScrollPos > nOldScrollPos ||
8079             nNewScrollPos < scrollInfo.nMin)
8080             nNewScrollPos = scrollInfo.nMin;
8081     }
8082
8083     /* set the new position, and reread in case it changed */
8084     scrollInfo.fMask = SIF_POS;
8085     scrollInfo.nPos = nNewScrollPos;
8086     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8087     
8088     /* carry on only if it really changed */
8089     if (nNewScrollPos == nOldScrollPos) return 0;
8090     
8091     /* now adjust to client coordinates */
8092     nScrollDiff = nOldScrollPos - nNewScrollPos;
8093     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8094    
8095     /* and scroll the window */ 
8096     scroll_list(infoPtr, 0, nScrollDiff);
8097
8098     return 0;
8099 }
8100
8101 /***
8102  * DESCRIPTION:
8103  * Performs horizontal scrolling.
8104  *
8105  * PARAMETER(S):
8106  * [I] infoPtr : valid pointer to the listview structure
8107  * [I] nScrollCode : scroll code
8108  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8109  * [I] hScrollWnd  : scrollbar control window handle
8110  *
8111  * RETURN:
8112  * Zero
8113  *
8114  * NOTES:
8115  *   SB_LINELEFT/SB_LINERIGHT:
8116  *        for LVS_ICON, LVS_SMALLICON  1 pixel
8117  *        for LVS_REPORT is 1 pixel
8118  *        for LVS_LIST  is 1 column --> which is a 1 because the
8119  *                                      scroll is based on columns not pixels
8120  *
8121  */
8122 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8123                                 INT nScrollDiff, HWND hScrollWnd)
8124 {
8125     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8126     INT nOldScrollPos, nNewScrollPos;
8127     SCROLLINFO scrollInfo;
8128
8129     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8130         debugscrollcode(nScrollCode), nScrollDiff);
8131
8132     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8133
8134     scrollInfo.cbSize = sizeof(SCROLLINFO);
8135     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8136
8137     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8138
8139     nOldScrollPos = scrollInfo.nPos;
8140
8141     switch (nScrollCode)
8142     {
8143     case SB_INTERNAL:
8144         break;
8145
8146     case SB_LINELEFT:
8147         nScrollDiff = -1;
8148         break;
8149
8150     case SB_LINERIGHT:
8151         nScrollDiff = 1;
8152         break;
8153
8154     case SB_PAGELEFT:
8155         nScrollDiff = -scrollInfo.nPage;
8156         break;
8157
8158     case SB_PAGERIGHT:
8159         nScrollDiff = scrollInfo.nPage;
8160         break;
8161
8162     case SB_THUMBPOSITION:
8163     case SB_THUMBTRACK:
8164         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8165         break;
8166
8167     default:
8168         nScrollDiff = 0;
8169     }
8170
8171     /* quit right away if pos isn't changing */
8172     if (nScrollDiff == 0) return 0;
8173     
8174     /* calculate new position, and handle overflows */
8175     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8176     if (nScrollDiff > 0) {
8177         if (nNewScrollPos < nOldScrollPos ||
8178             nNewScrollPos > scrollInfo.nMax)
8179             nNewScrollPos = scrollInfo.nMax;
8180     } else {
8181         if (nNewScrollPos > nOldScrollPos ||
8182             nNewScrollPos < scrollInfo.nMin)
8183             nNewScrollPos = scrollInfo.nMin;
8184     }
8185
8186     /* set the new position, and reread in case it changed */
8187     scrollInfo.fMask = SIF_POS;
8188     scrollInfo.nPos = nNewScrollPos;
8189     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8190     
8191     /* carry on only if it really changed */
8192     if (nNewScrollPos == nOldScrollPos) return 0;
8193     
8194     if(uView == LVS_REPORT)
8195         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8196       
8197     /* now adjust to client coordinates */
8198     nScrollDiff = nOldScrollPos - nNewScrollPos;
8199     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8200    
8201     /* and scroll the window */
8202     scroll_list(infoPtr, nScrollDiff, 0);
8203
8204   return 0;
8205 }
8206
8207 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8208 {
8209     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8210     INT gcWheelDelta = 0;
8211     INT pulScrollLines = 3;
8212     SCROLLINFO scrollInfo;
8213
8214     TRACE("(wheelDelta=%d)\n", wheelDelta);
8215
8216     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8217     gcWheelDelta -= wheelDelta;
8218
8219     scrollInfo.cbSize = sizeof(SCROLLINFO);
8220     scrollInfo.fMask = SIF_POS;
8221
8222     switch(uView)
8223     {
8224     case LVS_ICON:
8225     case LVS_SMALLICON:
8226        /*
8227         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8228         *  should be fixed in the future.
8229         */
8230         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8231                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8232         break;
8233
8234     case LVS_REPORT:
8235         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8236         {
8237             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8238             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8239             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8240         }
8241         break;
8242
8243     case LVS_LIST:
8244         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8245         break;
8246     }
8247     return 0;
8248 }
8249
8250 /***
8251  * DESCRIPTION:
8252  * ???
8253  *
8254  * PARAMETER(S):
8255  * [I] infoPtr : valid pointer to the listview structure
8256  * [I] nVirtualKey : virtual key
8257  * [I] lKeyData : key data
8258  *
8259  * RETURN:
8260  * Zero
8261  */
8262 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8263 {
8264   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8265   HWND hwndSelf = infoPtr->hwndSelf;
8266   INT nItem = -1;
8267   NMLVKEYDOWN nmKeyDown;
8268
8269   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8270
8271   /* send LVN_KEYDOWN notification */
8272   nmKeyDown.wVKey = nVirtualKey;
8273   nmKeyDown.flags = 0;
8274   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8275   if (!IsWindow(hwndSelf))
8276     return 0;
8277
8278   switch (nVirtualKey)
8279   {
8280   case VK_SPACE:
8281     nItem = infoPtr->nFocusedItem;
8282     break;
8283
8284   case VK_RETURN:
8285     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8286     {
8287         if (!notify(infoPtr, NM_RETURN)) return 0;
8288         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8289     }
8290     break;
8291
8292   case VK_HOME:
8293     if (infoPtr->nItemCount > 0)
8294       nItem = 0;
8295     break;
8296
8297   case VK_END:
8298     if (infoPtr->nItemCount > 0)
8299       nItem = infoPtr->nItemCount - 1;
8300     break;
8301
8302   case VK_LEFT:
8303     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8304     break;
8305
8306   case VK_UP:
8307     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8308     break;
8309
8310   case VK_RIGHT:
8311     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8312     break;
8313
8314   case VK_DOWN:
8315     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8316     break;
8317
8318   case VK_PRIOR:
8319     if (uView == LVS_REPORT)
8320     {
8321       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8322       if (infoPtr->nFocusedItem == topidx)
8323         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8324       else
8325         nItem = topidx;
8326     }
8327     else
8328       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8329                                     * LISTVIEW_GetCountPerRow(infoPtr);
8330     if(nItem < 0) nItem = 0;
8331     break;
8332
8333   case VK_NEXT:
8334     if (uView == LVS_REPORT)
8335     {
8336       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8337       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8338       if (infoPtr->nFocusedItem == topidx + cnt - 1)
8339         nItem = infoPtr->nFocusedItem + cnt - 1;
8340       else
8341         nItem = topidx + cnt - 1;
8342     }
8343     else
8344       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8345                                     * LISTVIEW_GetCountPerRow(infoPtr);
8346     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8347     break;
8348   }
8349
8350   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8351       LISTVIEW_KeySelection(infoPtr, nItem);
8352
8353   return 0;
8354 }
8355
8356 /***
8357  * DESCRIPTION:
8358  * Kills the focus.
8359  *
8360  * PARAMETER(S):
8361  * [I] infoPtr : valid pointer to the listview structure
8362  *
8363  * RETURN:
8364  * Zero
8365  */
8366 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8367 {
8368     TRACE("()\n");
8369
8370     /* if we did not have the focus, there's nothing to do */
8371     if (!infoPtr->bFocus) return 0;
8372    
8373     /* send NM_KILLFOCUS notification */
8374     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8375
8376     /* if we have a focus rectagle, get rid of it */
8377     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8378     
8379     /* set window focus flag */
8380     infoPtr->bFocus = FALSE;
8381
8382     /* invalidate the selected items before reseting focus flag */
8383     LISTVIEW_InvalidateSelectedItems(infoPtr);
8384     
8385     return 0;
8386 }
8387
8388 /***
8389  * DESCRIPTION:
8390  * Processes double click messages (left mouse button).
8391  *
8392  * PARAMETER(S):
8393  * [I] infoPtr : valid pointer to the listview structure
8394  * [I] wKey : key flag
8395  * [I] x,y : mouse coordinate
8396  *
8397  * RETURN:
8398  * Zero
8399  */
8400 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8401 {
8402     LVHITTESTINFO htInfo;
8403
8404     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8405     
8406     /* Cancel the item edition if any */
8407     if (infoPtr->itemEdit.fEnabled)
8408     {
8409       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8410       infoPtr->itemEdit.fEnabled = FALSE;
8411     }
8412
8413     /* send NM_RELEASEDCAPTURE notification */
8414     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8415
8416     htInfo.pt.x = x;
8417     htInfo.pt.y = y;
8418
8419     /* send NM_DBLCLK notification */
8420     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8421     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8422
8423     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8424     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8425
8426     return 0;
8427 }
8428
8429 /***
8430  * DESCRIPTION:
8431  * Processes mouse down messages (left mouse button).
8432  *
8433  * PARAMETERS:
8434  *   infoPtr  [I ] valid pointer to the listview structure
8435  *   wKey     [I ] key flag
8436  *   x,y      [I ] mouse coordinate
8437  *
8438  * RETURN:
8439  *   Zero
8440  */
8441 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8442 {
8443   LVHITTESTINFO lvHitTestInfo;
8444   static BOOL bGroupSelect = TRUE;
8445   BOOL bReceivedFocus = FALSE;
8446   POINT pt = { x, y };
8447   INT nItem;
8448
8449   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8450
8451   /* send NM_RELEASEDCAPTURE notification */
8452   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8453
8454   if (!infoPtr->bFocus)
8455   {
8456     bReceivedFocus = TRUE;
8457     SetFocus(infoPtr->hwndSelf);
8458   }
8459
8460   /* set left button down flag and record the click position */
8461   infoPtr->bLButtonDown = TRUE;
8462   infoPtr->ptClickPos = pt;
8463
8464   lvHitTestInfo.pt.x = x;
8465   lvHitTestInfo.pt.y = y;
8466
8467   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8468   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8469   infoPtr->nEditLabelItem = -1;
8470   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8471   {
8472     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8473     {
8474         DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8475         if(state == 1 || state == 2)
8476         {
8477             LVITEMW lvitem;
8478             state ^= 3;
8479             lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8480             lvitem.stateMask = LVIS_STATEIMAGEMASK;
8481             LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8482         }
8483         return 0;
8484     }
8485
8486     if (infoPtr->dwStyle & LVS_SINGLESEL)
8487     {
8488       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8489         infoPtr->nEditLabelItem = nItem;
8490       else
8491         LISTVIEW_SetSelection(infoPtr, nItem);
8492     }
8493     else
8494     {
8495       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8496       {
8497         if (bGroupSelect)
8498         {
8499           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8500           LISTVIEW_SetItemFocus(infoPtr, nItem);
8501           infoPtr->nSelectionMark = nItem;
8502         }
8503         else
8504         {
8505           LVITEMW item;
8506
8507           item.state = LVIS_SELECTED | LVIS_FOCUSED;
8508           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8509
8510           LISTVIEW_SetItemState(infoPtr,nItem,&item);
8511           infoPtr->nSelectionMark = nItem;
8512         }
8513       }
8514       else if (wKey & MK_CONTROL)
8515       {
8516         LVITEMW item;
8517
8518         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8519         
8520         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8521         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8522         LISTVIEW_SetItemState(infoPtr, nItem, &item);
8523         infoPtr->nSelectionMark = nItem;
8524       }
8525       else  if (wKey & MK_SHIFT)
8526       {
8527         LISTVIEW_SetGroupSelection(infoPtr, nItem);
8528       }
8529       else
8530       {
8531         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8532           infoPtr->nEditLabelItem = nItem;
8533
8534         /* set selection (clears other pre-existing selections) */
8535         LISTVIEW_SetSelection(infoPtr, nItem);
8536       }
8537     }
8538   }
8539   else
8540   {
8541     /* remove all selections */
8542     LISTVIEW_DeselectAll(infoPtr);
8543     ReleaseCapture();
8544   }
8545   
8546   if (bReceivedFocus)
8547     infoPtr->nEditLabelItem = -1;
8548
8549   return 0;
8550 }
8551
8552 /***
8553  * DESCRIPTION:
8554  * Processes mouse up messages (left mouse button).
8555  *
8556  * PARAMETERS:
8557  *   infoPtr [I ] valid pointer to the listview structure
8558  *   wKey    [I ] key flag
8559  *   x,y     [I ] mouse coordinate
8560  *
8561  * RETURN:
8562  *   Zero
8563  */
8564 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8565 {
8566     LVHITTESTINFO lvHitTestInfo;
8567     
8568     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8569
8570     if (!infoPtr->bLButtonDown) return 0;
8571
8572     lvHitTestInfo.pt.x = x;
8573     lvHitTestInfo.pt.y = y;
8574
8575     /* send NM_CLICK notification */
8576     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8577     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8578
8579     /* set left button flag */
8580     infoPtr->bLButtonDown = FALSE;
8581
8582     /* if we clicked on a selected item, edit the label */
8583     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8584     {
8585         /* we want to make sure the user doesn't want to do a double click. So we will
8586          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8587          */
8588         infoPtr->itemEdit.fEnabled = TRUE;
8589         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8590         SetTimer(infoPtr->hwndSelf,
8591             (UINT_PTR)&infoPtr->itemEdit,
8592             GetDoubleClickTime(),
8593             LISTVIEW_DelayedEditItem);
8594     }
8595
8596     return 0;
8597 }
8598
8599 /***
8600  * DESCRIPTION:
8601  * Destroys the listview control (called after WM_DESTROY).
8602  *
8603  * PARAMETER(S):
8604  * [I] infoPtr : valid pointer to the listview structure
8605  *
8606  * RETURN:
8607  * Zero
8608  */
8609 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8610 {
8611   TRACE("()\n");
8612
8613   /* delete all items */
8614   LISTVIEW_DeleteAllItems(infoPtr);
8615
8616   /* destroy data structure */
8617   DPA_Destroy(infoPtr->hdpaItems);
8618   DPA_Destroy(infoPtr->hdpaPosX);
8619   DPA_Destroy(infoPtr->hdpaPosY);
8620   DPA_Destroy(infoPtr->hdpaColumns);
8621   ranges_destroy(infoPtr->selectionRanges);
8622
8623   /* destroy image lists */
8624   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8625   {
8626       if (infoPtr->himlNormal)
8627           ImageList_Destroy(infoPtr->himlNormal);
8628       if (infoPtr->himlSmall)
8629           ImageList_Destroy(infoPtr->himlSmall);
8630       if (infoPtr->himlState)
8631           ImageList_Destroy(infoPtr->himlState);
8632   }
8633
8634   /* destroy font, bkgnd brush */
8635   infoPtr->hFont = 0;
8636   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8637   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8638
8639   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8640
8641   /* free listview info pointer*/
8642   Free(infoPtr);
8643
8644   return 0;
8645 }
8646
8647 /***
8648  * DESCRIPTION:
8649  * Handles notifications from header.
8650  *
8651  * PARAMETER(S):
8652  * [I] infoPtr : valid pointer to the listview structure
8653  * [I] nCtrlId : control identifier
8654  * [I] lpnmh : notification information
8655  *
8656  * RETURN:
8657  * Zero
8658  */
8659 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8660 {
8661     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8662     HWND hwndSelf = infoPtr->hwndSelf;
8663     
8664     TRACE("(lpnmh=%p)\n", lpnmh);
8665
8666     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8667     
8668     switch (lpnmh->hdr.code)
8669     {    
8670         case HDN_TRACKW:
8671         case HDN_TRACKA:
8672         {
8673             COLUMN_INFO *lpColumnInfo;
8674             POINT ptOrigin;
8675             INT x;
8676             
8677             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8678                 break;
8679
8680             /* remove the old line (if any) */
8681             LISTVIEW_DrawTrackLine(infoPtr);
8682             
8683             /* compute & draw the new line */
8684             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8685             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8686             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8687             infoPtr->xTrackLine = x + ptOrigin.x;
8688             LISTVIEW_DrawTrackLine(infoPtr);
8689             break;
8690         }
8691         
8692         case HDN_ENDTRACKA:
8693         case HDN_ENDTRACKW:
8694             /* remove the track line (if any) */
8695             LISTVIEW_DrawTrackLine(infoPtr);
8696             infoPtr->xTrackLine = -1;
8697             break;
8698             
8699         case HDN_ENDDRAG:
8700             FIXME("Changing column order not implemented\n");
8701             return TRUE;
8702             
8703         case HDN_ITEMCHANGINGW:
8704         case HDN_ITEMCHANGINGA:
8705             return notify_forward_header(infoPtr, lpnmh);
8706             
8707         case HDN_ITEMCHANGEDW:
8708         case HDN_ITEMCHANGEDA:
8709         {
8710             COLUMN_INFO *lpColumnInfo;
8711             INT dx, cxy;
8712             
8713             notify_forward_header(infoPtr, lpnmh);
8714             if (!IsWindow(hwndSelf))
8715                 break;
8716
8717             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8718             {
8719                 HDITEMW hdi;
8720     
8721                 hdi.mask = HDI_WIDTH;
8722                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8723                 cxy = hdi.cxy;
8724             }
8725             else
8726                 cxy = lpnmh->pitem->cxy;
8727             
8728             /* determine how much we change since the last know position */
8729             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8730             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8731             if (dx != 0)
8732             {
8733                 lpColumnInfo->rcHeader.right += dx;
8734                 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8735                     LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8736                 else
8737                 {
8738                     /* only needs to update the scrolls */
8739                     infoPtr->nItemWidth += dx;
8740                     LISTVIEW_UpdateScroll(infoPtr);
8741                 }
8742                 LISTVIEW_UpdateItemSize(infoPtr);
8743                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8744                 {
8745                     POINT ptOrigin;
8746                     RECT rcCol = lpColumnInfo->rcHeader;
8747                     
8748                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8749                     OffsetRect(&rcCol, ptOrigin.x, 0);
8750                     
8751                     rcCol.top = infoPtr->rcList.top;
8752                     rcCol.bottom = infoPtr->rcList.bottom;
8753
8754                     /* resizing left-aligned columns leaves most of the left side untouched */
8755                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8756                     {
8757                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8758                         if (dx > 0)
8759                             nMaxDirty += dx;
8760                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8761                     }
8762
8763                     /* when shrinking the last column clear the now unused field */
8764                     if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8765                         rcCol.right -= dx;
8766
8767                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8768                 }
8769             }
8770         }
8771         break;
8772
8773         case HDN_ITEMCLICKW:
8774         case HDN_ITEMCLICKA:
8775         {
8776             /* Handle sorting by Header Column */
8777             NMLISTVIEW nmlv;
8778
8779             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8780             nmlv.iItem = -1;
8781             nmlv.iSubItem = lpnmh->iItem;
8782             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8783         }
8784         break;
8785
8786         case HDN_DIVIDERDBLCLICKW:
8787         case HDN_DIVIDERDBLCLICKA:
8788             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8789             break;
8790     }
8791
8792     return 0;
8793 }
8794
8795 /***
8796  * DESCRIPTION:
8797  * Paint non-client area of control.
8798  *
8799  * PARAMETER(S):
8800  * [I] infoPtr : valid pointer to the listview structureof the sender
8801  * [I] region : update region
8802  *
8803  * RETURN:
8804  *  TRUE  - frame was painted
8805  *  FALSE - call default window proc
8806  */
8807 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
8808 {
8809     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8810     HDC dc;
8811     RECT r;
8812     HRGN cliprgn;
8813     int cxEdge = GetSystemMetrics (SM_CXEDGE),
8814         cyEdge = GetSystemMetrics (SM_CYEDGE);
8815
8816     if (!theme) return FALSE;
8817
8818     GetWindowRect(infoPtr->hwndSelf, &r);
8819
8820     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8821         r.right - cxEdge, r.bottom - cyEdge);
8822     if (region != (HRGN)1)
8823         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8824     OffsetRect(&r, -r.left, -r.top);
8825
8826     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8827     OffsetRect(&r, -r.left, -r.top);
8828
8829     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8830         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8831     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8832     ReleaseDC(infoPtr->hwndSelf, dc);
8833
8834     /* Call default proc to get the scrollbars etc. painted */
8835     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8836
8837     return TRUE;
8838 }
8839
8840 /***
8841  * DESCRIPTION:
8842  * Determines the type of structure to use.
8843  *
8844  * PARAMETER(S):
8845  * [I] infoPtr : valid pointer to the listview structureof the sender
8846  * [I] hwndFrom : listview window handle
8847  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8848  *
8849  * RETURN:
8850  * Zero
8851  */
8852 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8853 {
8854     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8855
8856     if (nCommand != NF_REQUERY) return 0;
8857     
8858     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8859     
8860     return 0;
8861 }
8862
8863 /***
8864  * DESCRIPTION:
8865  * Paints/Repaints the listview control.
8866  *
8867  * PARAMETER(S):
8868  * [I] infoPtr : valid pointer to the listview structure
8869  * [I] hdc : device context handle
8870  *
8871  * RETURN:
8872  * Zero
8873  */
8874 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8875 {
8876     TRACE("(hdc=%p)\n", hdc);
8877
8878     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8879     {
8880         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8881         
8882         infoPtr->bNoItemMetrics = FALSE;
8883         LISTVIEW_UpdateItemSize(infoPtr);
8884         if (uView == LVS_ICON || uView == LVS_SMALLICON)
8885             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8886         LISTVIEW_UpdateScroll(infoPtr);
8887     }
8888
8889     UpdateWindow(infoPtr->hwndHeader);
8890
8891     if (hdc) 
8892         LISTVIEW_Refresh(infoPtr, hdc, NULL);
8893     else
8894     {
8895         PAINTSTRUCT ps;
8896
8897         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8898         if (!hdc) return 1;
8899         LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
8900         EndPaint(infoPtr->hwndSelf, &ps);
8901     }
8902
8903     return 0;
8904 }
8905
8906
8907 /***
8908  * DESCRIPTION:
8909  * Paints/Repaints the listview control.
8910  *
8911  * PARAMETER(S):
8912  * [I] infoPtr : valid pointer to the listview structure
8913  * [I] hdc : device context handle
8914  * [I] options : drawing options
8915  *
8916  * RETURN:
8917  * Zero
8918  */
8919 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8920 {
8921     FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
8922
8923     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8924         return 0;
8925
8926     if (options & PRF_ERASEBKGND)
8927         LISTVIEW_EraseBkgnd(infoPtr, hdc);
8928
8929     if (options & PRF_CLIENT)
8930         LISTVIEW_Paint(infoPtr, hdc);
8931
8932     return 0;
8933 }
8934
8935
8936 /***
8937  * DESCRIPTION:
8938  * Processes double click messages (right mouse button).
8939  *
8940  * PARAMETER(S):
8941  * [I] infoPtr : valid pointer to the listview structure
8942  * [I] wKey : key flag
8943  * [I] x,y : mouse coordinate
8944  *
8945  * RETURN:
8946  * Zero
8947  */
8948 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8949 {
8950     LVHITTESTINFO lvHitTestInfo;
8951     
8952     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8953
8954     /* send NM_RELEASEDCAPTURE notification */
8955     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8956
8957     /* send NM_RDBLCLK notification */
8958     lvHitTestInfo.pt.x = x;
8959     lvHitTestInfo.pt.y = y;
8960     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8961     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8962
8963     return 0;
8964 }
8965
8966 /***
8967  * DESCRIPTION:
8968  * Processes mouse down messages (right mouse button).
8969  *
8970  * PARAMETER(S):
8971  * [I] infoPtr : valid pointer to the listview structure
8972  * [I] wKey : key flag
8973  * [I] x,y : mouse coordinate
8974  *
8975  * RETURN:
8976  * Zero
8977  */
8978 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8979 {
8980     LVHITTESTINFO lvHitTestInfo;
8981     INT nItem;
8982
8983     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8984
8985     /* send NM_RELEASEDCAPTURE notification */
8986     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8987
8988     /* make sure the listview control window has the focus */
8989     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8990
8991     /* set right button down flag */
8992     infoPtr->bRButtonDown = TRUE;
8993
8994     /* determine the index of the selected item */
8995     lvHitTestInfo.pt.x = x;
8996     lvHitTestInfo.pt.y = y;
8997     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8998   
8999     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9000     {
9001         LISTVIEW_SetItemFocus(infoPtr, nItem);
9002         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9003             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9004             LISTVIEW_SetSelection(infoPtr, nItem);
9005     }
9006     else
9007     {
9008         LISTVIEW_DeselectAll(infoPtr);
9009     }
9010
9011     return 0;
9012 }
9013
9014 /***
9015  * DESCRIPTION:
9016  * Processes mouse up messages (right mouse button).
9017  *
9018  * PARAMETER(S):
9019  * [I] infoPtr : valid pointer to the listview structure
9020  * [I] wKey : key flag
9021  * [I] x,y : mouse coordinate
9022  *
9023  * RETURN:
9024  * Zero
9025  */
9026 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9027 {
9028     LVHITTESTINFO lvHitTestInfo;
9029     POINT pt;
9030
9031     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9032
9033     if (!infoPtr->bRButtonDown) return 0;
9034  
9035     /* set button flag */
9036     infoPtr->bRButtonDown = FALSE;
9037
9038     /* Send NM_RClICK notification */
9039     lvHitTestInfo.pt.x = x;
9040     lvHitTestInfo.pt.y = y;
9041     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9042     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9043
9044     /* Change to screen coordinate for WM_CONTEXTMENU */
9045     pt = lvHitTestInfo.pt;
9046     ClientToScreen(infoPtr->hwndSelf, &pt);
9047
9048     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9049     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9050                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9051
9052     return 0;
9053 }
9054
9055
9056 /***
9057  * DESCRIPTION:
9058  * Sets the cursor.
9059  *
9060  * PARAMETER(S):
9061  * [I] infoPtr : valid pointer to the listview structure
9062  * [I] hwnd : window handle of window containing the cursor
9063  * [I] nHittest : hit-test code
9064  * [I] wMouseMsg : ideintifier of the mouse message
9065  *
9066  * RETURN:
9067  * TRUE if cursor is set
9068  * FALSE otherwise
9069  */
9070 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9071 {
9072     LVHITTESTINFO lvHitTestInfo;
9073
9074     if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
9075
9076     if(!infoPtr->hHotCursor)  return FALSE;
9077
9078     GetCursorPos(&lvHitTestInfo.pt);
9079     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9080
9081     SetCursor(infoPtr->hHotCursor);
9082
9083     return TRUE;
9084 }
9085
9086 /***
9087  * DESCRIPTION:
9088  * Sets the focus.
9089  *
9090  * PARAMETER(S):
9091  * [I] infoPtr : valid pointer to the listview structure
9092  * [I] hwndLoseFocus : handle of previously focused window
9093  *
9094  * RETURN:
9095  * Zero
9096  */
9097 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9098 {
9099     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9100
9101     /* if we have the focus already, there's nothing to do */
9102     if (infoPtr->bFocus) return 0;
9103    
9104     /* send NM_SETFOCUS notification */
9105     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9106
9107     /* set window focus flag */
9108     infoPtr->bFocus = TRUE;
9109
9110     /* put the focus rect back on */
9111     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9112
9113     /* redraw all visible selected items */
9114     LISTVIEW_InvalidateSelectedItems(infoPtr);
9115
9116     return 0;
9117 }
9118
9119 /***
9120  * DESCRIPTION:
9121  * Sets the font.
9122  *
9123  * PARAMETER(S):
9124  * [I] infoPtr : valid pointer to the listview structure
9125  * [I] fRedraw : font handle
9126  * [I] fRedraw : redraw flag
9127  *
9128  * RETURN:
9129  * Zero
9130  */
9131 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9132 {
9133     HFONT oldFont = infoPtr->hFont;
9134
9135     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9136
9137     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9138     if (infoPtr->hFont == oldFont) return 0;
9139     
9140     LISTVIEW_SaveTextMetrics(infoPtr);
9141
9142     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9143         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9144
9145     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9146
9147     return 0;
9148 }
9149
9150 /***
9151  * DESCRIPTION:
9152  * Message handling for WM_SETREDRAW.
9153  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9154  *
9155  * PARAMETER(S):
9156  * [I] infoPtr : valid pointer to the listview structure
9157  * [I] bRedraw: state of redraw flag
9158  *
9159  * RETURN:
9160  * DefWinProc return value
9161  */
9162 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9163 {
9164     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9165
9166     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9167     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9168
9169     infoPtr->bRedraw = bRedraw;
9170
9171     if(!bRedraw) return 0;
9172     
9173     if (is_autoarrange(infoPtr))
9174         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9175     LISTVIEW_UpdateScroll(infoPtr);
9176
9177     /* despite what the WM_SETREDRAW docs says, apps expect us
9178      * to invalidate the listview here... stupid! */
9179     LISTVIEW_InvalidateList(infoPtr);
9180
9181     return 0;
9182 }
9183
9184 /***
9185  * DESCRIPTION:
9186  * Resizes the listview control. This function processes WM_SIZE
9187  * messages.  At this time, the width and height are not used.
9188  *
9189  * PARAMETER(S):
9190  * [I] infoPtr : valid pointer to the listview structure
9191  * [I] Width : new width
9192  * [I] Height : new height
9193  *
9194  * RETURN:
9195  * Zero
9196  */
9197 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9198 {
9199     RECT rcOld = infoPtr->rcList;
9200
9201     TRACE("(width=%d, height=%d)\n", Width, Height);
9202
9203     LISTVIEW_UpdateSize(infoPtr);
9204     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9205   
9206     /* do not bother with display related stuff if we're not redrawing */ 
9207     if (!is_redrawing(infoPtr)) return 0;
9208     
9209     if (is_autoarrange(infoPtr)) 
9210         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9211
9212     LISTVIEW_UpdateScroll(infoPtr);
9213
9214     /* refresh all only for lists whose height changed significantly */
9215     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
9216         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9217         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9218         LISTVIEW_InvalidateList(infoPtr);
9219
9220   return 0;
9221 }
9222
9223 /***
9224  * DESCRIPTION:
9225  * Sets the size information.
9226  *
9227  * PARAMETER(S):
9228  * [I] infoPtr : valid pointer to the listview structure
9229  *
9230  * RETURN:
9231  *  None
9232  */
9233 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9234 {
9235     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9236
9237     TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9238     
9239     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9240
9241     if (uView == LVS_LIST)
9242     {
9243         /* Apparently the "LIST" style is supposed to have the same
9244          * number of items in a column even if there is no scroll bar.
9245          * Since if a scroll bar already exists then the bottom is already
9246          * reduced, only reduce if the scroll bar does not currently exist.
9247          * The "2" is there to mimic the native control. I think it may be
9248          * related to either padding or edges.  (GLA 7/2002)
9249          */
9250         if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9251             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9252         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9253     }
9254     else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9255     {
9256         HDLAYOUT hl;
9257         WINDOWPOS wp;
9258
9259         hl.prc = &infoPtr->rcList;
9260         hl.pwpos = &wp;
9261         SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9262
9263         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9264
9265         infoPtr->rcList.top = max(wp.cy, 0);
9266     }
9267
9268     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9269 }
9270
9271 /***
9272  * DESCRIPTION:
9273  * Processes WM_STYLECHANGED messages.
9274  *
9275  * PARAMETER(S):
9276  * [I] infoPtr : valid pointer to the listview structure
9277  * [I] wStyleType : window style type (normal or extended)
9278  * [I] lpss : window style information
9279  *
9280  * RETURN:
9281  * Zero
9282  */
9283 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9284                                  const STYLESTRUCT *lpss)
9285 {
9286     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9287     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9288
9289     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9290           wStyleType, lpss->styleOld, lpss->styleNew);
9291
9292     if (wStyleType != GWL_STYLE) return 0;
9293   
9294     /* FIXME: if LVS_NOSORTHEADER changed, update header */
9295     /*        what if LVS_OWNERDATA changed? */
9296     /*        or LVS_SINGLESEL */
9297     /*        or LVS_SORT{AS,DES}CENDING */
9298
9299     infoPtr->dwStyle = lpss->styleNew;
9300
9301     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9302         ((lpss->styleNew & WS_HSCROLL) == 0))
9303        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9304
9305     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9306         ((lpss->styleNew & WS_VSCROLL) == 0))
9307        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9308
9309     if (uNewView != uOldView)
9310     {
9311         SIZE oldIconSize = infoPtr->iconSize;
9312         HIMAGELIST himl;
9313     
9314         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9315         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9316
9317         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9318         SetRectEmpty(&infoPtr->rcFocus);
9319
9320         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9321         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9322     
9323         if (uNewView == LVS_ICON)
9324         {
9325             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9326             {
9327                 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9328                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9329                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9330             }
9331         }
9332         else if (uNewView == LVS_REPORT)
9333         {
9334             HDLAYOUT hl;
9335             WINDOWPOS wp;
9336
9337             hl.prc = &infoPtr->rcList;
9338             hl.pwpos = &wp;
9339             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9340             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9341         }
9342
9343         LISTVIEW_UpdateItemSize(infoPtr);
9344     }
9345
9346     if (uNewView == LVS_REPORT)
9347         ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9348      
9349     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9350          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9351          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9352
9353     /* update the size of the client area */
9354     LISTVIEW_UpdateSize(infoPtr);
9355
9356     /* add scrollbars if needed */
9357     LISTVIEW_UpdateScroll(infoPtr);
9358
9359     /* invalidate client area + erase background */
9360     LISTVIEW_InvalidateList(infoPtr);
9361
9362     return 0;
9363 }
9364
9365 /***
9366  * DESCRIPTION:
9367  * Window procedure of the listview control.
9368  *
9369  */
9370 static LRESULT WINAPI
9371 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9372 {
9373   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9374
9375   TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9376
9377   if (!infoPtr && (uMsg != WM_NCCREATE))
9378     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9379
9380   switch (uMsg)
9381   {
9382   case LVM_APPROXIMATEVIEWRECT:
9383     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9384                                         LOWORD(lParam), HIWORD(lParam));
9385   case LVM_ARRANGE:
9386     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9387
9388 /* case LVM_CANCELEDITLABEL: */
9389
9390   case LVM_CREATEDRAGIMAGE:
9391     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9392
9393   case LVM_DELETEALLITEMS:
9394     return LISTVIEW_DeleteAllItems(infoPtr);
9395
9396   case LVM_DELETECOLUMN:
9397     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9398
9399   case LVM_DELETEITEM:
9400     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9401
9402   case LVM_EDITLABELW:
9403     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9404
9405   case LVM_EDITLABELA:
9406     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9407
9408   /* case LVM_ENABLEGROUPVIEW: */
9409
9410   case LVM_ENSUREVISIBLE:
9411     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9412
9413   case LVM_FINDITEMW:
9414     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9415
9416   case LVM_FINDITEMA:
9417     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9418
9419   case LVM_GETBKCOLOR:
9420     return infoPtr->clrBk;
9421
9422   /* case LVM_GETBKIMAGE: */
9423
9424   case LVM_GETCALLBACKMASK:
9425     return infoPtr->uCallbackMask;
9426
9427   case LVM_GETCOLUMNA:
9428     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9429
9430   case LVM_GETCOLUMNW:
9431     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9432
9433   case LVM_GETCOLUMNORDERARRAY:
9434     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9435
9436   case LVM_GETCOLUMNWIDTH:
9437     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9438
9439   case LVM_GETCOUNTPERPAGE:
9440     return LISTVIEW_GetCountPerPage(infoPtr);
9441
9442   case LVM_GETEDITCONTROL:
9443     return (LRESULT)infoPtr->hwndEdit;
9444
9445   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9446     return infoPtr->dwLvExStyle;
9447
9448   /* case LVM_GETGROUPINFO: */
9449
9450   /* case LVM_GETGROUPMETRICS: */
9451
9452   case LVM_GETHEADER:
9453     return (LRESULT)infoPtr->hwndHeader;
9454
9455   case LVM_GETHOTCURSOR:
9456     return (LRESULT)infoPtr->hHotCursor;
9457
9458   case LVM_GETHOTITEM:
9459     return infoPtr->nHotItem;
9460
9461   case LVM_GETHOVERTIME:
9462     return infoPtr->dwHoverTime;
9463
9464   case LVM_GETIMAGELIST:
9465     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9466
9467   /* case LVM_GETINSERTMARK: */
9468
9469   /* case LVM_GETINSERTMARKCOLOR: */
9470
9471   /* case LVM_GETINSERTMARKRECT: */
9472
9473   case LVM_GETISEARCHSTRINGA:
9474   case LVM_GETISEARCHSTRINGW:
9475     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9476     return FALSE;
9477
9478   case LVM_GETITEMA:
9479     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9480
9481   case LVM_GETITEMW:
9482     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9483
9484   case LVM_GETITEMCOUNT:
9485     return infoPtr->nItemCount;
9486
9487   case LVM_GETITEMPOSITION:
9488     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9489
9490   case LVM_GETITEMRECT:
9491     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9492
9493   case LVM_GETITEMSPACING:
9494     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9495
9496   case LVM_GETITEMSTATE:
9497     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9498
9499   case LVM_GETITEMTEXTA:
9500     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9501
9502   case LVM_GETITEMTEXTW:
9503     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9504
9505   case LVM_GETNEXTITEM:
9506     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9507
9508   case LVM_GETNUMBEROFWORKAREAS:
9509     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9510     return 1;
9511
9512   case LVM_GETORIGIN:
9513     if (!lParam) return FALSE;
9514     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9515     return TRUE;
9516
9517   /* case LVM_GETOUTLINECOLOR: */
9518
9519   /* case LVM_GETSELECTEDCOLUMN: */
9520
9521   case LVM_GETSELECTEDCOUNT:
9522     return LISTVIEW_GetSelectedCount(infoPtr);
9523
9524   case LVM_GETSELECTIONMARK:
9525     return infoPtr->nSelectionMark;
9526
9527   case LVM_GETSTRINGWIDTHA:
9528     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9529
9530   case LVM_GETSTRINGWIDTHW:
9531     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9532
9533   case LVM_GETSUBITEMRECT:
9534     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9535
9536   case LVM_GETTEXTBKCOLOR:
9537     return infoPtr->clrTextBk;
9538
9539   case LVM_GETTEXTCOLOR:
9540     return infoPtr->clrText;
9541
9542   /* case LVM_GETTILEINFO: */
9543
9544   /* case LVM_GETTILEVIEWINFO: */
9545
9546   case LVM_GETTOOLTIPS:
9547     if( !infoPtr->hwndToolTip )
9548         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9549     return (LRESULT)infoPtr->hwndToolTip;
9550
9551   case LVM_GETTOPINDEX:
9552     return LISTVIEW_GetTopIndex(infoPtr);
9553
9554   /*case LVM_GETUNICODEFORMAT:
9555     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9556     return FALSE;*/
9557
9558   /* case LVM_GETVIEW: */
9559
9560   case LVM_GETVIEWRECT:
9561     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9562
9563   case LVM_GETWORKAREAS:
9564     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9565     return FALSE;
9566
9567   /* case LVM_HASGROUP: */
9568
9569   case LVM_HITTEST:
9570     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9571
9572   case LVM_INSERTCOLUMNA:
9573     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9574
9575   case LVM_INSERTCOLUMNW:
9576     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9577
9578   /* case LVM_INSERTGROUP: */
9579
9580   /* case LVM_INSERTGROUPSORTED: */
9581
9582   case LVM_INSERTITEMA:
9583     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9584
9585   case LVM_INSERTITEMW:
9586     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9587
9588   /* case LVM_INSERTMARKHITTEST: */
9589
9590   /* case LVM_ISGROUPVIEWENABLED: */
9591
9592   /* case LVM_MAPIDTOINDEX: */
9593
9594   /* case LVM_MAPINDEXTOID: */
9595
9596   /* case LVM_MOVEGROUP: */
9597
9598   /* case LVM_MOVEITEMTOGROUP: */
9599
9600   case LVM_REDRAWITEMS:
9601     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9602
9603   /* case LVM_REMOVEALLGROUPS: */
9604
9605   /* case LVM_REMOVEGROUP: */
9606
9607   case LVM_SCROLL:
9608     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9609
9610   case LVM_SETBKCOLOR:
9611     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9612
9613   /* case LVM_SETBKIMAGE: */
9614
9615   case LVM_SETCALLBACKMASK:
9616     infoPtr->uCallbackMask = (UINT)wParam;
9617     return TRUE;
9618
9619   case LVM_SETCOLUMNA:
9620     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9621
9622   case LVM_SETCOLUMNW:
9623     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9624
9625   case LVM_SETCOLUMNORDERARRAY:
9626     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9627
9628   case LVM_SETCOLUMNWIDTH:
9629     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9630
9631   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9632     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9633
9634   /* case LVM_SETGROUPINFO: */
9635
9636   /* case LVM_SETGROUPMETRICS: */
9637
9638   case LVM_SETHOTCURSOR:
9639     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9640
9641   case LVM_SETHOTITEM:
9642     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9643
9644   case LVM_SETHOVERTIME:
9645     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9646
9647   case LVM_SETICONSPACING:
9648     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9649
9650   case LVM_SETIMAGELIST:
9651     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9652
9653   /* case LVM_SETINFOTIP: */
9654
9655   /* case LVM_SETINSERTMARK: */
9656
9657   /* case LVM_SETINSERTMARKCOLOR: */
9658
9659   case LVM_SETITEMA:
9660     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9661
9662   case LVM_SETITEMW:
9663     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9664
9665   case LVM_SETITEMCOUNT:
9666     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9667
9668   case LVM_SETITEMPOSITION:
9669     {
9670         POINT pt;
9671         pt.x = (short)LOWORD(lParam);
9672         pt.y = (short)HIWORD(lParam);
9673         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9674     }
9675
9676   case LVM_SETITEMPOSITION32:
9677     if (lParam == 0) return FALSE;
9678     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9679
9680   case LVM_SETITEMSTATE:
9681     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9682
9683   case LVM_SETITEMTEXTA:
9684     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9685
9686   case LVM_SETITEMTEXTW:
9687     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9688
9689   /* case LVM_SETOUTLINECOLOR: */
9690
9691   /* case LVM_SETSELECTEDCOLUMN: */
9692
9693   case LVM_SETSELECTIONMARK:
9694     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9695
9696   case LVM_SETTEXTBKCOLOR:
9697     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9698
9699   case LVM_SETTEXTCOLOR:
9700     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9701
9702   /* case LVM_SETTILEINFO: */
9703
9704   /* case LVM_SETTILEVIEWINFO: */
9705
9706   /* case LVM_SETTILEWIDTH: */
9707
9708   case LVM_SETTOOLTIPS:
9709     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9710
9711   case LVM_SETUNICODEFORMAT:
9712     return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9713
9714   /* case LVM_SETVIEW: */
9715
9716   /* case LVM_SETWORKAREAS: */
9717
9718   /* case LVM_SORTGROUPS: */
9719
9720   case LVM_SORTITEMS:
9721     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9722
9723   /* LVM_SORTITEMSEX: */
9724
9725   case LVM_SUBITEMHITTEST:
9726     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9727
9728   case LVM_UPDATE:
9729     return LISTVIEW_Update(infoPtr, (INT)wParam);
9730
9731   case WM_CHAR:
9732     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9733
9734   case WM_COMMAND:
9735     return LISTVIEW_Command(infoPtr, wParam, lParam);
9736
9737   case WM_NCCREATE:
9738     return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9739
9740   case WM_CREATE:
9741     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9742
9743   case WM_DESTROY:
9744     return LISTVIEW_Destroy(infoPtr);
9745
9746   case WM_ENABLE:
9747     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9748
9749   case WM_ERASEBKGND:
9750     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9751
9752   case WM_GETDLGCODE:
9753     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9754
9755   case WM_GETFONT:
9756     return (LRESULT)infoPtr->hFont;
9757
9758   case WM_HSCROLL:
9759     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9760
9761   case WM_KEYDOWN:
9762     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9763
9764   case WM_KILLFOCUS:
9765     return LISTVIEW_KillFocus(infoPtr);
9766
9767   case WM_LBUTTONDBLCLK:
9768     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9769
9770   case WM_LBUTTONDOWN:
9771     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9772
9773   case WM_LBUTTONUP:
9774     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9775
9776   case WM_MOUSEMOVE:
9777     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9778
9779   case WM_MOUSEHOVER:
9780     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9781
9782   case WM_NCDESTROY:
9783     return LISTVIEW_NCDestroy(infoPtr);
9784
9785   case WM_NCPAINT:
9786     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9787         return 0;
9788     goto fwd_msg;
9789
9790   case WM_NOTIFY:
9791     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9792         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9793     else return 0;
9794
9795   case WM_NOTIFYFORMAT:
9796     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9797
9798   case WM_PRINTCLIENT:
9799     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9800
9801   case WM_PAINT:
9802     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9803
9804   case WM_RBUTTONDBLCLK:
9805     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9806
9807   case WM_RBUTTONDOWN:
9808     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9809
9810   case WM_RBUTTONUP:
9811     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9812
9813   case WM_SETCURSOR:
9814     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9815       return TRUE;
9816     goto fwd_msg;
9817
9818   case WM_SETFOCUS:
9819     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9820
9821   case WM_SETFONT:
9822     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9823
9824   case WM_SETREDRAW:
9825     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9826
9827   case WM_SIZE:
9828     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9829
9830   case WM_STYLECHANGED:
9831     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9832
9833   case WM_SYSCOLORCHANGE:
9834     COMCTL32_RefreshSysColors();
9835     return 0;
9836
9837 /*      case WM_TIMER: */
9838   case WM_THEMECHANGED:
9839     return LISTVIEW_ThemeChanged(infoPtr);
9840
9841   case WM_VSCROLL:
9842     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9843
9844   case WM_MOUSEWHEEL:
9845       if (wParam & (MK_SHIFT | MK_CONTROL))
9846           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9847       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9848
9849   case WM_WINDOWPOSCHANGED:
9850       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
9851       {
9852       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9853           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9854                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9855
9856       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9857       {
9858           MEASUREITEMSTRUCT mis;
9859           mis.CtlType = ODT_LISTVIEW;
9860           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9861           mis.itemID = -1;
9862           mis.itemWidth = 0;
9863           mis.itemData = 0;
9864           mis.itemHeight= infoPtr->nItemHeight;
9865           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9866           if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9867               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9868       }
9869
9870           LISTVIEW_UpdateSize(infoPtr);
9871           LISTVIEW_UpdateScroll(infoPtr);
9872       }
9873       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9874
9875 /*      case WM_WININICHANGE: */
9876
9877   default:
9878     if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9879       ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
9880
9881   fwd_msg:
9882     /* call default window procedure */
9883     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9884   }
9885
9886 }
9887
9888 /***
9889  * DESCRIPTION:
9890  * Registers the window class.
9891  *
9892  * PARAMETER(S):
9893  * None
9894  *
9895  * RETURN:
9896  * None
9897  */
9898 void LISTVIEW_Register(void)
9899 {
9900     WNDCLASSW wndClass;
9901
9902     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9903     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9904     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9905     wndClass.cbClsExtra = 0;
9906     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9907     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9908     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9909     wndClass.lpszClassName = WC_LISTVIEWW;
9910     RegisterClassW(&wndClass);
9911 }
9912
9913 /***
9914  * DESCRIPTION:
9915  * Unregisters the window class.
9916  *
9917  * PARAMETER(S):
9918  * None
9919  *
9920  * RETURN:
9921  * None
9922  */
9923 void LISTVIEW_Unregister(void)
9924 {
9925     UnregisterClassW(WC_LISTVIEWW, NULL);
9926 }
9927
9928 /***
9929  * DESCRIPTION:
9930  * Handle any WM_COMMAND messages
9931  *
9932  * PARAMETER(S):
9933  * [I] infoPtr : valid pointer to the listview structure
9934  * [I] wParam : the first message parameter
9935  * [I] lParam : the second message parameter
9936  *
9937  * RETURN:
9938  *   Zero.
9939  */
9940 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9941 {
9942     switch (HIWORD(wParam))
9943     {
9944         case EN_UPDATE:
9945         {
9946             /*
9947              * Adjust the edit window size
9948              */
9949             WCHAR buffer[1024];
9950             HDC           hdc = GetDC(infoPtr->hwndEdit);
9951             HFONT         hFont, hOldFont = 0;
9952             RECT          rect;
9953             SIZE          sz;
9954             int           len;
9955
9956             if (!infoPtr->hwndEdit || !hdc) return 0;
9957             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9958             GetWindowRect(infoPtr->hwndEdit, &rect);
9959
9960             /* Select font to get the right dimension of the string */
9961             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9962             if(hFont != 0)
9963             {
9964                 hOldFont = SelectObject(hdc, hFont);
9965             }
9966
9967             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9968             {
9969                 TEXTMETRICW textMetric;
9970
9971                 /* Add Extra spacing for the next character */
9972                 GetTextMetricsW(hdc, &textMetric);
9973                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9974
9975                 SetWindowPos (
9976                     infoPtr->hwndEdit,
9977                     HWND_TOP,
9978                     0,
9979                     0,
9980                     sz.cx,
9981                     rect.bottom - rect.top,
9982                     SWP_DRAWFRAME|SWP_NOMOVE);
9983             }
9984             if(hFont != 0)
9985                 SelectObject(hdc, hOldFont);
9986
9987             ReleaseDC(infoPtr->hwndEdit, hdc);
9988
9989             break;
9990         }
9991
9992         default:
9993           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9994     }
9995
9996     return 0;
9997 }
9998
9999
10000 /***
10001  * DESCRIPTION:
10002  * Subclassed edit control windproc function
10003  *
10004  * PARAMETER(S):
10005  * [I] hwnd : the edit window handle
10006  * [I] uMsg : the message that is to be processed
10007  * [I] wParam : first message parameter
10008  * [I] lParam : second message parameter
10009  * [I] isW : TRUE if input is Unicode
10010  *
10011  * RETURN:
10012  *   Zero.
10013  */
10014 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10015 {
10016     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10017     BOOL cancel = FALSE;
10018
10019     TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10020           hwnd, uMsg, wParam, lParam, isW);
10021
10022     switch (uMsg)
10023     {
10024         case WM_GETDLGCODE:
10025           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10026
10027         case WM_KILLFOCUS:
10028             break;
10029
10030         case WM_DESTROY:
10031         {
10032             WNDPROC editProc = infoPtr->EditWndProc;
10033             infoPtr->EditWndProc = 0;
10034             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10035             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10036         }
10037
10038         case WM_KEYDOWN:
10039             if (VK_ESCAPE == (INT)wParam)
10040             {
10041                 cancel = TRUE;
10042                 break;
10043             }
10044             else if (VK_RETURN == (INT)wParam)
10045                 break;
10046
10047         default:
10048             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10049     }
10050
10051     /* kill the edit */
10052     if (infoPtr->hwndEdit)
10053     {
10054         LPWSTR buffer = NULL;
10055
10056         infoPtr->hwndEdit = 0;
10057         if (!cancel)
10058         {
10059             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10060
10061             if (len)
10062             {
10063                 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10064                 {
10065                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
10066                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10067                 }
10068             }
10069         }
10070         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10071
10072         Free(buffer);
10073     }
10074
10075     SendMessageW(hwnd, WM_CLOSE, 0, 0);
10076     return 0;
10077 }
10078
10079 /***
10080  * DESCRIPTION:
10081  * Subclassed edit control Unicode windproc function
10082  *
10083  * PARAMETER(S):
10084  * [I] hwnd : the edit window handle
10085  * [I] uMsg : the message that is to be processed
10086  * [I] wParam : first message parameter
10087  * [I] lParam : second message parameter
10088  *
10089  * RETURN:
10090  */
10091 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10092 {
10093     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10094 }
10095
10096 /***
10097  * DESCRIPTION:
10098  * Subclassed edit control ANSI windproc function
10099  *
10100  * PARAMETER(S):
10101  * [I] hwnd : the edit window handle
10102  * [I] uMsg : the message that is to be processed
10103  * [I] wParam : first message parameter
10104  * [I] lParam : second message parameter
10105  *
10106  * RETURN:
10107  */
10108 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10109 {
10110     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10111 }
10112
10113 /***
10114  * DESCRIPTION:
10115  * Creates a subclassed edit cotrol
10116  *
10117  * PARAMETER(S):
10118  * [I] infoPtr : valid pointer to the listview structure
10119  * [I] text : initial text for the edit
10120  * [I] style : the window style
10121  * [I] isW : TRUE if input is Unicode
10122  *
10123  * RETURN:
10124  */
10125 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10126         INT x, INT y, INT width, INT height, BOOL isW)
10127 {
10128     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10129     HWND hedit;
10130     SIZE sz;
10131     HDC hdc;
10132     HDC hOldFont=0;
10133     TEXTMETRICW textMetric;
10134     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10135
10136     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10137
10138     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10139     hdc = GetDC(infoPtr->hwndSelf);
10140
10141     /* Select the font to get appropriate metric dimensions */
10142     if(infoPtr->hFont != 0)
10143         hOldFont = SelectObject(hdc, infoPtr->hFont);
10144
10145     /*Get String Length in pixels */
10146     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10147
10148     /*Add Extra spacing for the next character */
10149     GetTextMetricsW(hdc, &textMetric);
10150     sz.cx += (textMetric.tmMaxCharWidth * 2);
10151
10152     if(infoPtr->hFont != 0)
10153         SelectObject(hdc, hOldFont);
10154
10155     ReleaseDC(infoPtr->hwndSelf, hdc);
10156     if (isW)
10157         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10158     else
10159         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10160
10161     if (!hedit) return 0;
10162
10163     infoPtr->EditWndProc = (WNDPROC)
10164         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10165                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10166
10167     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10168
10169     return hedit;
10170 }