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