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