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