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