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