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