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