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