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