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