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