comctl32: Replace nonprintable characters with spaces on SB_SETTEXT.
[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);
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);
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);
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  *
3224  * RETURN:
3225  *   SUCCESS : TRUE (needs to be repainted)
3226  *   FAILURE : FALSE (nothing has changed)
3227  */
3228 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3229 {
3230   /* FIXME: pass in the state */
3231   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3232   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3233   BOOL bResult = FALSE;
3234
3235   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3236   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3237   {
3238     if (infoPtr->dwStyle & LVS_SINGLESEL)
3239     {
3240       bResult = TRUE;
3241       LISTVIEW_SetSelection(infoPtr, nItem);
3242     }
3243     else
3244     {
3245       if (wShift)
3246       {
3247         bResult = TRUE;
3248         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3249       }
3250       else if (wCtrl)
3251       {
3252         LVITEMW lvItem;
3253         lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3254         lvItem.stateMask = LVIS_SELECTED;
3255         LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3256
3257         if (lvItem.state & LVIS_SELECTED)
3258             infoPtr->nSelectionMark = nItem;
3259
3260         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3261       }
3262       else
3263       {
3264         bResult = TRUE;
3265         LISTVIEW_SetSelection(infoPtr, nItem);
3266       }
3267     }
3268     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3269   }
3270
3271   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3272   return bResult;
3273 }
3274
3275 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3276 {
3277     LVHITTESTINFO lvHitTestInfo;
3278
3279     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3280     lvHitTestInfo.pt.x = pt.x;
3281     lvHitTestInfo.pt.y = pt.y;
3282
3283     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3284
3285     lpLVItem->mask = LVIF_PARAM;
3286     lpLVItem->iItem = lvHitTestInfo.iItem;
3287     lpLVItem->iSubItem = 0;
3288
3289     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3290 }
3291
3292 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3293 {
3294     return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3295             (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3296             (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3297 }
3298
3299 /***
3300  * DESCRIPTION:
3301  * Called when the mouse is being actively tracked and has hovered for a specified
3302  * amount of time
3303  *
3304  * PARAMETER(S):
3305  * [I] infoPtr : valid pointer to the listview structure
3306  * [I] fwKeys : key indicator
3307  * [I] x,y : mouse position
3308  *
3309  * RETURN:
3310  *   0 if the message was processed, non-zero if there was an error
3311  *
3312  * INFO:
3313  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3314  * over the item for a certain period of time.
3315  *
3316  */
3317 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3318 {
3319     if (LISTVIEW_isHotTracking(infoPtr))
3320     {
3321         LVITEMW item;
3322         POINT pt;
3323
3324         pt.x = x;
3325         pt.y = y;
3326
3327         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3328             LISTVIEW_SetSelection(infoPtr, item.iItem);
3329     }
3330
3331     return 0;
3332 }
3333
3334 /***
3335  * DESCRIPTION:
3336  * Called whenever WM_MOUSEMOVE is received.
3337  *
3338  * PARAMETER(S):
3339  * [I] infoPtr : valid pointer to the listview structure
3340  * [I] fwKeys : key indicator
3341  * [I] x,y : mouse position
3342  *
3343  * RETURN:
3344  *   0 if the message is processed, non-zero if there was an error
3345  */
3346 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3347 {
3348     TRACKMOUSEEVENT trackinfo;
3349
3350     if (!(fwKeys & MK_LBUTTON))
3351         infoPtr->bLButtonDown = FALSE;
3352
3353     if (infoPtr->bLButtonDown)
3354     {
3355         POINT tmp;
3356         RECT rect;
3357         WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3358         WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3359
3360         rect.left = infoPtr->ptClickPos.x - wDragWidth;
3361         rect.right = infoPtr->ptClickPos.x + wDragWidth;
3362         rect.top = infoPtr->ptClickPos.y - wDragHeight;
3363         rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3364
3365         tmp.x = x;
3366         tmp.y = y;
3367
3368         if (!PtInRect(&rect, tmp))
3369         {
3370             LVHITTESTINFO lvHitTestInfo;
3371             NMLISTVIEW nmlv;
3372
3373             lvHitTestInfo.pt = infoPtr->ptClickPos;
3374             LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3375
3376             ZeroMemory(&nmlv, sizeof(nmlv));
3377             nmlv.iItem = lvHitTestInfo.iItem;
3378             nmlv.ptAction = infoPtr->ptClickPos;
3379
3380             if (!infoPtr->bDragging)
3381             {
3382                 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3383                 infoPtr->bDragging = TRUE;
3384             }
3385
3386             return 0;
3387         }
3388     }
3389     else
3390         infoPtr->bLButtonDown = FALSE;
3391
3392     /* see if we are supposed to be tracking mouse hovering */
3393     if (LISTVIEW_isHotTracking(infoPtr)) {
3394         /* fill in the trackinfo struct */
3395         trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3396         trackinfo.dwFlags = TME_QUERY;
3397         trackinfo.hwndTrack = infoPtr->hwndSelf;
3398         trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3399
3400         /* see if we are already tracking this hwnd */
3401         _TrackMouseEvent(&trackinfo);
3402
3403         if(!(trackinfo.dwFlags & TME_HOVER)) {
3404             trackinfo.dwFlags = TME_HOVER;
3405
3406             /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3407             _TrackMouseEvent(&trackinfo);
3408         }
3409     }
3410
3411     return 0;
3412 }
3413
3414
3415 /***
3416  * Tests whether the item is assignable to a list with style lStyle
3417  */
3418 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3419 {
3420     if ( (lpLVItem->mask & LVIF_TEXT) && 
3421         (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3422         (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3423     
3424     return TRUE;
3425 }
3426
3427
3428 /***
3429  * DESCRIPTION:
3430  * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3431  *
3432  * PARAMETER(S):
3433  * [I] infoPtr : valid pointer to the listview structure
3434  * [I] lpLVItem : valid pointer to new item attributes
3435  * [I] isNew : the item being set is being inserted
3436  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3437  * [O] bChanged : will be set to TRUE if the item really changed
3438  *
3439  * RETURN:
3440  *   SUCCESS : TRUE
3441  *   FAILURE : FALSE
3442  */
3443 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3444 {
3445     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3446     ITEM_INFO *lpItem;
3447     NMLISTVIEW nmlv;
3448     UINT uChanged = 0;
3449     LVITEMW item;
3450
3451     TRACE("()\n");
3452
3453     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3454     
3455     if (lpLVItem->mask == 0) return TRUE;   
3456
3457     if (infoPtr->dwStyle & LVS_OWNERDATA)
3458     {
3459         /* a virtual listview only stores selection and focus */
3460         if (lpLVItem->mask & ~LVIF_STATE)
3461             return FALSE;
3462         lpItem = NULL;
3463     }
3464     else
3465     {
3466         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3467         lpItem = DPA_GetPtr(hdpaSubItems, 0);
3468         assert (lpItem);
3469     }
3470
3471     /* we need to get the lParam and state of the item */
3472     item.iItem = lpLVItem->iItem;
3473     item.iSubItem = lpLVItem->iSubItem;
3474     item.mask = LVIF_STATE | LVIF_PARAM;
3475     item.stateMask = ~0;
3476     item.state = 0;
3477     item.lParam = 0;
3478     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3479
3480     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3481     /* determine what fields will change */    
3482     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3483         uChanged |= LVIF_STATE;
3484
3485     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3486         uChanged |= LVIF_IMAGE;
3487
3488     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3489         uChanged |= LVIF_PARAM;
3490
3491     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3492         uChanged |= LVIF_INDENT;
3493
3494     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3495         uChanged |= LVIF_TEXT;
3496    
3497     TRACE("uChanged=0x%x\n", uChanged); 
3498     if (!uChanged) return TRUE;
3499     *bChanged = TRUE;
3500     
3501     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3502     nmlv.iItem = lpLVItem->iItem;
3503     nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3504     nmlv.uOldState = item.state;
3505     nmlv.uChanged = uChanged;
3506     nmlv.lParam = item.lParam;
3507     
3508     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3509     /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3510     /* are enabled */
3511     if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3512     {
3513       HWND hwndSelf = infoPtr->hwndSelf;
3514
3515       if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3516         return FALSE;
3517       if (!IsWindow(hwndSelf))
3518         return FALSE;
3519     }
3520
3521     /* copy information */
3522     if (lpLVItem->mask & LVIF_TEXT)
3523         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3524
3525     if (lpLVItem->mask & LVIF_IMAGE)
3526         lpItem->hdr.iImage = lpLVItem->iImage;
3527
3528     if (lpLVItem->mask & LVIF_PARAM)
3529         lpItem->lParam = lpLVItem->lParam;
3530
3531     if (lpLVItem->mask & LVIF_INDENT)
3532         lpItem->iIndent = lpLVItem->iIndent;
3533
3534     if (uChanged & LVIF_STATE)
3535     {
3536         if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3537         {
3538             lpItem->state &= ~lpLVItem->stateMask;
3539             lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3540         }
3541         if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3542         {
3543             if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3544             ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3545         }
3546         else if (lpLVItem->stateMask & LVIS_SELECTED)
3547             ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3548         
3549         /* if we are asked to change focus, and we manage it, do it */
3550         if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3551         {
3552             if (lpLVItem->state & LVIS_FOCUSED)
3553             {
3554                 LISTVIEW_SetItemFocus(infoPtr, -1);
3555                 infoPtr->nFocusedItem = lpLVItem->iItem;
3556                 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3557             }
3558             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3559                 infoPtr->nFocusedItem = -1;
3560         }
3561     }
3562
3563     /* if we're inserting the item, we're done */
3564     if (isNew) return TRUE;
3565     
3566     /* send LVN_ITEMCHANGED notification */
3567     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3568     if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3569
3570     return TRUE;
3571 }
3572
3573 /***
3574  * DESCRIPTION:
3575  * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3576  *
3577  * PARAMETER(S):
3578  * [I] infoPtr : valid pointer to the listview structure
3579  * [I] lpLVItem : valid pointer to new subitem attributes
3580  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3581  * [O] bChanged : will be set to TRUE if the item really changed
3582  *
3583  * RETURN:
3584  *   SUCCESS : TRUE
3585  *   FAILURE : FALSE
3586  */
3587 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3588 {
3589     HDPA hdpaSubItems;
3590     SUBITEM_INFO *lpSubItem;
3591
3592     /* we do not support subitems for virtual listviews */
3593     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3594     
3595     /* set subitem only if column is present */
3596     if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3597    
3598     /* First do some sanity checks */
3599     /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3600        particularly useful. We currently do not actually do anything with
3601        the flag on subitems.
3602     */
3603     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3604     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3605    
3606     /* get the subitem structure, and create it if not there */
3607     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3608     assert (hdpaSubItems);
3609     
3610     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3611     if (!lpSubItem)
3612     {
3613         SUBITEM_INFO *tmpSubItem;
3614         INT i;
3615
3616         lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3617         if (!lpSubItem) return FALSE;
3618         /* we could binary search here, if need be...*/
3619         for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3620         {
3621             tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3622             if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3623         }
3624         if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3625         {
3626             Free(lpSubItem);
3627             return FALSE;
3628         }
3629         lpSubItem->iSubItem = lpLVItem->iSubItem;
3630         lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3631         *bChanged = TRUE;
3632     }
3633     
3634     if (lpLVItem->mask & LVIF_IMAGE)
3635         if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3636         {
3637             lpSubItem->hdr.iImage = lpLVItem->iImage;
3638             *bChanged = TRUE;
3639         }
3640
3641     if (lpLVItem->mask & LVIF_TEXT)
3642         if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3643         {
3644             textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3645             *bChanged = TRUE;
3646         }
3647
3648     return TRUE;
3649 }
3650
3651 /***
3652  * DESCRIPTION:
3653  * Sets item attributes.
3654  *
3655  * PARAMETER(S):
3656  * [I] infoPtr : valid pointer to the listview structure
3657  * [I] lpLVItem : new item attributes
3658  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3659  *
3660  * RETURN:
3661  *   SUCCESS : TRUE
3662  *   FAILURE : FALSE
3663  */
3664 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3665 {
3666     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3667     HWND hwndSelf = infoPtr->hwndSelf;
3668     LPWSTR pszText = NULL;
3669     BOOL bResult, bChanged = FALSE;
3670
3671     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3672
3673     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3674         return FALSE;
3675
3676     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3677     if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3678     {
3679         pszText = lpLVItem->pszText;
3680         lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3681     }
3682
3683     /* actually set the fields */
3684     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3685
3686     if (lpLVItem->iSubItem)
3687         bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3688     else
3689         bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3690     if (!IsWindow(hwndSelf))
3691         return FALSE;
3692
3693     /* redraw item, if necessary */
3694     if (bChanged && !infoPtr->bIsDrawing)
3695     {
3696         /* this little optimization eliminates some nasty flicker */
3697         if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3698              !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3699              lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3700             LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3701         else
3702             LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3703     }
3704     /* restore text */
3705     if (pszText)
3706     {
3707         textfreeT(lpLVItem->pszText, isW);
3708         lpLVItem->pszText = pszText;
3709     }
3710
3711     return bResult;
3712 }
3713
3714 /***
3715  * DESCRIPTION:
3716  * Retrieves the index of the item at coordinate (0, 0) of the client area.
3717  *
3718  * PARAMETER(S):
3719  * [I] infoPtr : valid pointer to the listview structure
3720  *
3721  * RETURN:
3722  * item index
3723  */
3724 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3725 {
3726     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3727     INT nItem = 0;
3728     SCROLLINFO scrollInfo;
3729
3730     scrollInfo.cbSize = sizeof(SCROLLINFO);
3731     scrollInfo.fMask = SIF_POS;
3732
3733     if (uView == LVS_LIST)
3734     {
3735         if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3736             nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3737     }
3738     else if (uView == LVS_REPORT)
3739     {
3740         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3741             nItem = scrollInfo.nPos;
3742     } 
3743     else
3744     {
3745         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3746             nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3747     }
3748
3749     TRACE("nItem=%d\n", nItem);
3750     
3751     return nItem;
3752 }
3753
3754
3755 /***
3756  * DESCRIPTION:
3757  * Erases the background of the given rectangle
3758  *
3759  * PARAMETER(S):
3760  * [I] infoPtr : valid pointer to the listview structure
3761  * [I] hdc : device context handle
3762  * [I] lprcBox : clipping rectangle
3763  *
3764  * RETURN:
3765  *   Success: TRUE
3766  *   Failure: FALSE
3767  */
3768 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3769 {
3770     if (!infoPtr->hBkBrush) return FALSE;
3771
3772     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3773
3774     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3775 }
3776
3777 /***
3778  * DESCRIPTION:
3779  * Draws an item.
3780  *
3781  * PARAMETER(S):
3782  * [I] infoPtr : valid pointer to the listview structure
3783  * [I] hdc : device context handle
3784  * [I] nItem : item index
3785  * [I] nSubItem : subitem index
3786  * [I] pos : item position in client coordinates
3787  * [I] cdmode : custom draw mode
3788  *
3789  * RETURN:
3790  *   Success: TRUE
3791  *   Failure: FALSE
3792  */
3793 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3794 {
3795     UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3796     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3797     static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3798     DWORD cdsubitemmode = CDRF_DODEFAULT;
3799     LPRECT lprcFocus;
3800     RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3801     NMLVCUSTOMDRAW nmlvcd;
3802     HIMAGELIST himl;
3803     LVITEMW lvItem;
3804     HFONT hOldFont;
3805
3806     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3807
3808     /* get information needed for drawing the item */
3809     lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3810     if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3811     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3812     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3813     lvItem.iItem = nItem;
3814     lvItem.iSubItem = nSubItem;
3815     lvItem.state = 0;
3816     lvItem.lParam = 0;
3817     lvItem.cchTextMax = DISP_TEXT_SIZE;
3818     lvItem.pszText = szDispText;
3819     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3820     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
3821         lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3822     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3823     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3824
3825     /* now check if we need to update the focus rectangle */
3826     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3827
3828     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3829     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3830     OffsetRect(&rcBox, pos.x, pos.y);
3831     OffsetRect(&rcSelect, pos.x, pos.y);
3832     OffsetRect(&rcIcon, pos.x, pos.y);
3833     OffsetRect(&rcStateIcon, pos.x, pos.y);
3834     OffsetRect(&rcLabel, pos.x, pos.y);
3835     TRACE("    rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3836         wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3837         wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3838
3839     /* fill in the custom draw structure */
3840     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3841
3842     hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3843     if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3844     if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3845     if (cdmode & CDRF_NOTIFYITEMDRAW)
3846         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3847     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3848     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3849     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3850     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3851     {
3852         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3853         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3854     }
3855     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3856         prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3857     else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3858         prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3859
3860     /* in full row select, subitems, will just use main item's colors */
3861     if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3862         nmlvcd.clrTextBk = CLR_NONE;
3863
3864     /* state icons */
3865     if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3866     {
3867         UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3868         if (uStateImage)
3869         {
3870              TRACE("uStateImage=%d\n", uStateImage);
3871              ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3872                  rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3873         }
3874     }
3875
3876     /* small icons */
3877     himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3878     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3879     {
3880         TRACE("iImage=%d\n", lvItem.iImage);
3881         ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3882                          rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3883                          (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3884     }
3885
3886     /* Don't bother painting item being edited */
3887     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3888
3889     /* FIXME: temporary hack */
3890     rcSelect.left = rcLabel.left;
3891
3892     /* draw the selection background, if we're drawing the main item */
3893     if (nSubItem == 0)
3894     {
3895         /* in icon mode, the label rect is really what we want to draw the
3896          * background for */
3897         if (uView == LVS_ICON)
3898             rcSelect = rcLabel;
3899
3900         if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3901             rcSelect.right = rcBox.right;
3902
3903         if (nmlvcd.clrTextBk != CLR_NONE) 
3904             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3905         if(lprcFocus) *lprcFocus = rcSelect;
3906     }
3907    
3908     /* figure out the text drawing flags */
3909     uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3910     if (uView == LVS_ICON)
3911         uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3912     else if (nSubItem)
3913     {
3914         switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3915         {
3916         case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
3917         case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3918         default:            uFormat |= DT_LEFT;
3919         }
3920     }
3921     if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3922     {
3923         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3924         else rcLabel.left += LABEL_HOR_PADDING;
3925     }
3926     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3927
3928     /* for GRIDLINES reduce the bottom so the text formats correctly */
3929     if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3930         rcLabel.bottom--;
3931
3932     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3933
3934 postpaint:
3935     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3936         notify_postpaint(infoPtr, &nmlvcd);
3937     if (cdsubitemmode & CDRF_NEWFONT)
3938         SelectObject(hdc, hOldFont);
3939     return TRUE;
3940 }
3941
3942 /***
3943  * DESCRIPTION:
3944  * Draws listview items when in owner draw mode.
3945  *
3946  * PARAMETER(S):
3947  * [I] infoPtr : valid pointer to the listview structure
3948  * [I] hdc : device context handle
3949  *
3950  * RETURN:
3951  * None
3952  */
3953 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3954 {
3955     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3956     DWORD cditemmode = CDRF_DODEFAULT;
3957     NMLVCUSTOMDRAW nmlvcd;
3958     POINT Origin, Position;
3959     DRAWITEMSTRUCT dis;
3960     LVITEMW item;
3961     
3962     TRACE("()\n");
3963
3964     ZeroMemory(&dis, sizeof(dis));
3965     
3966     /* Get scroll info once before loop */
3967     LISTVIEW_GetOrigin(infoPtr, &Origin);
3968     
3969     /* iterate through the invalidated rows */
3970     while(iterator_next(i))
3971     {
3972         item.iItem = i->nItem;
3973         item.iSubItem = 0;
3974         item.mask = LVIF_PARAM | LVIF_STATE;
3975         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3976         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3977            
3978         dis.CtlType = ODT_LISTVIEW;
3979         dis.CtlID = uID;
3980         dis.itemID = item.iItem;
3981         dis.itemAction = ODA_DRAWENTIRE;
3982         dis.itemState = 0;
3983         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3984         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3985         dis.hwndItem = infoPtr->hwndSelf;
3986         dis.hDC = hdc;
3987         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3988         dis.rcItem.left = Position.x + Origin.x;
3989         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3990         dis.rcItem.top = Position.y + Origin.y;
3991         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3992         dis.itemData = item.lParam;
3993
3994         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3995
3996     /*
3997      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3998      * structure for the rest. of the paint cycle
3999      */
4000         customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4001         if (cdmode & CDRF_NOTIFYITEMDRAW)
4002             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4003     
4004         if (!(cditemmode & CDRF_SKIPDEFAULT))
4005         {
4006             prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4007             SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4008         }
4009
4010         if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4011             notify_postpaint(infoPtr, &nmlvcd);
4012     }
4013 }
4014
4015 /***
4016  * DESCRIPTION:
4017  * Draws listview items when in report display mode.
4018  *
4019  * PARAMETER(S):
4020  * [I] infoPtr : valid pointer to the listview structure
4021  * [I] hdc : device context handle
4022  * [I] cdmode : custom draw mode
4023  *
4024  * RETURN:
4025  * None
4026  */
4027 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4028 {
4029     INT rgntype;
4030     RECT rcClip, rcItem;
4031     POINT Origin, Position;
4032     RANGE colRange;
4033     ITERATOR j;
4034
4035     TRACE("()\n");
4036
4037     /* figure out what to draw */
4038     rgntype = GetClipBox(hdc, &rcClip);
4039     if (rgntype == NULLREGION) return;
4040     
4041     /* Get scroll info once before loop */
4042     LISTVIEW_GetOrigin(infoPtr, &Origin);
4043     
4044     /* narrow down the columns we need to paint */
4045     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4046     {
4047         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4048         if (rcItem.right + Origin.x >= rcClip.left) break;
4049     }
4050     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4051     {
4052         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4053         if (rcItem.left + Origin.x < rcClip.right) break;
4054     }
4055     iterator_rangeitems(&j, colRange);
4056
4057     /* in full row select, we _have_ to draw the main item */
4058     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4059         j.nSpecial = 0;
4060
4061     /* iterate through the invalidated rows */
4062     while(iterator_next(i))
4063     {
4064         /* iterate through the invalidated columns */
4065         while(iterator_next(&j))
4066         {
4067             LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4068             Position.x += Origin.x;
4069             Position.y += Origin.y;
4070
4071             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4072             {
4073                 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4074                 rcItem.top = 0;
4075                 rcItem.bottom = infoPtr->nItemHeight;
4076                 OffsetRect(&rcItem, Position.x, Position.y);
4077                 if (!RectVisible(hdc, &rcItem)) continue;
4078             }
4079
4080             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4081         }
4082     }
4083     iterator_destroy(&j);
4084 }
4085
4086 /***
4087  * DESCRIPTION:
4088  * Draws the gridlines if necessary when in report display mode.
4089  *
4090  * PARAMETER(S):
4091  * [I] infoPtr : valid pointer to the listview structure
4092  * [I] hdc : device context handle
4093  *
4094  * RETURN:
4095  * None
4096  */
4097 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4098 {
4099     INT rgntype;
4100     INT y, itemheight;
4101     HPEN hPen, hOldPen;
4102     RECT rcClip, rcItem;
4103     POINT Origin;
4104     RANGE colRange;
4105     ITERATOR j;
4106
4107     TRACE("()\n");
4108
4109     /* figure out what to draw */
4110     rgntype = GetClipBox(hdc, &rcClip);
4111     if (rgntype == NULLREGION) return;
4112
4113     /* Get scroll info once before loop */
4114     LISTVIEW_GetOrigin(infoPtr, &Origin);
4115
4116     /* narrow down the columns we need to paint */
4117     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4118     {
4119         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4120         if (rcItem.right + Origin.x >= rcClip.left) break;
4121     }
4122     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4123     {
4124         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4125         if (rcItem.left + Origin.x < rcClip.right) break;
4126     }
4127     iterator_rangeitems(&j, colRange);
4128
4129     if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4130     {
4131         hOldPen = SelectObject ( hdc, hPen );
4132
4133         /* draw the vertical lines for the columns */
4134         iterator_rangeitems(&j, colRange);
4135         while(iterator_next(&j))
4136         {
4137             LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4138             if (rcItem.left == 0) continue; /* skip first column */
4139             rcItem.left += Origin.x;
4140             rcItem.right += Origin.x;
4141             rcItem.top = infoPtr->rcList.top;
4142             rcItem.bottom = infoPtr->rcList.bottom;
4143             TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4144             MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4145             LineTo (hdc, rcItem.left, rcItem.bottom);
4146         }
4147         iterator_destroy(&j);
4148
4149         /* draw the horizontial lines for the rows */
4150         itemheight =  LISTVIEW_CalculateItemHeight(infoPtr);
4151         rcItem.left = infoPtr->rcList.left + Origin.x;
4152         rcItem.right = infoPtr->rcList.right + Origin.x;
4153         rcItem.bottom = rcItem.top = Origin.y - 1;
4154         MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4155         LineTo(hdc, rcItem.right, rcItem.top);
4156         for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4157         {
4158             rcItem.bottom = rcItem.top = y;
4159             TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4160             MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4161             LineTo (hdc, rcItem.right, rcItem.top);
4162         }
4163
4164         SelectObject( hdc, hOldPen );
4165         DeleteObject( hPen );
4166     }
4167 }
4168
4169 /***
4170  * DESCRIPTION:
4171  * Draws listview items when in list display mode.
4172  *
4173  * PARAMETER(S):
4174  * [I] infoPtr : valid pointer to the listview structure
4175  * [I] hdc : device context handle
4176  * [I] cdmode : custom draw mode
4177  *
4178  * RETURN:
4179  * None
4180  */
4181 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4182 {
4183     POINT Origin, Position;
4184
4185     /* Get scroll info once before loop */
4186     LISTVIEW_GetOrigin(infoPtr, &Origin);
4187     
4188     while(iterator_prev(i))
4189     {
4190         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4191         Position.x += Origin.x;
4192         Position.y += Origin.y;
4193
4194         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4195     }
4196 }
4197
4198
4199 /***
4200  * DESCRIPTION:
4201  * Draws listview items.
4202  *
4203  * PARAMETER(S):
4204  * [I] infoPtr : valid pointer to the listview structure
4205  * [I] hdc : device context handle
4206  * [I] prcErase : rect to be erased before refresh (may be NULL)
4207  *
4208  * RETURN:
4209  * NoneX
4210  */
4211 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4212 {
4213     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4214     COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4215     NMLVCUSTOMDRAW nmlvcd;
4216     HFONT hOldFont = 0;
4217     DWORD cdmode;
4218     INT oldBkMode = 0;
4219     RECT rcClient;
4220     ITERATOR i;
4221     HDC hdcOrig = hdc;
4222     HBITMAP hbmp = NULL;
4223
4224     LISTVIEW_DUMP(infoPtr);
4225
4226     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4227         TRACE("double buffering\n");
4228
4229         hdc = CreateCompatibleDC(hdcOrig);
4230         if (!hdc) {
4231             ERR("Failed to create DC for backbuffer\n");
4232             return;
4233         }
4234         hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4235                                       infoPtr->rcList.bottom);
4236         if (!hbmp) {
4237             ERR("Failed to create bitmap for backbuffer\n");
4238             DeleteDC(hdc);
4239             return;
4240         }
4241
4242         SelectObject(hdc, hbmp);
4243         SelectObject(hdc, infoPtr->hFont);
4244     } else {
4245         /* Save dc values we're gonna trash while drawing
4246          * FIXME: Should be done in LISTVIEW_DrawItem() */
4247         hOldFont = SelectObject(hdc, infoPtr->hFont);
4248         oldBkMode = GetBkMode(hdc);
4249         oldBkColor = GetBkColor(hdc);
4250         oldTextColor = GetTextColor(hdc);
4251     }
4252
4253     infoPtr->bIsDrawing = TRUE;
4254
4255     if (prcErase) {
4256         LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4257     } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4258         /* If no erasing was done (usually because RedrawWindow was called
4259          * with RDW_INVALIDATE only) we need to copy the old contents into
4260          * the backbuffer before continuing. */
4261         BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4262                infoPtr->rcList.right - infoPtr->rcList.left,
4263                infoPtr->rcList.bottom - infoPtr->rcList.top,
4264                hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4265     }
4266
4267     /* FIXME: Shouldn't need to do this */
4268     oldClrTextBk = infoPtr->clrTextBk;
4269     oldClrText   = infoPtr->clrText;
4270    
4271     infoPtr->cditemmode = CDRF_DODEFAULT;
4272
4273     GetClientRect(infoPtr->hwndSelf, &rcClient);
4274     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4275     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4276     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4277     prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4278
4279     /* Use these colors to draw the items */
4280     infoPtr->clrTextBk = nmlvcd.clrTextBk;
4281     infoPtr->clrText = nmlvcd.clrText;
4282
4283     /* nothing to draw */
4284     if(infoPtr->nItemCount == 0) goto enddraw;
4285
4286     /* figure out what we need to draw */
4287     iterator_visibleitems(&i, infoPtr, hdc);
4288     
4289     /* send cache hint notification */
4290     if (infoPtr->dwStyle & LVS_OWNERDATA)
4291     {
4292         RANGE range = iterator_range(&i);
4293         NMLVCACHEHINT nmlv;
4294         
4295         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4296         nmlv.iFrom = range.lower;
4297         nmlv.iTo   = range.upper - 1;
4298         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4299     }
4300
4301     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4302         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4303     else
4304     {
4305         if (uView == LVS_REPORT)
4306             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4307         else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4308             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4309
4310         /* if we have a focus rect, draw it */
4311         if (infoPtr->bFocus)
4312             DrawFocusRect(hdc, &infoPtr->rcFocus);
4313     }
4314     iterator_destroy(&i);
4315     
4316 enddraw:
4317     /* For LVS_EX_GRIDLINES go and draw lines */
4318     /*  This includes the case where there were *no* items */
4319     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4320         infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4321         LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4322
4323     if (cdmode & CDRF_NOTIFYPOSTPAINT)
4324         notify_postpaint(infoPtr, &nmlvcd);
4325
4326     infoPtr->clrTextBk = oldClrTextBk;
4327     infoPtr->clrText = oldClrText;
4328
4329     if(hbmp) {
4330         BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4331                infoPtr->rcList.right - infoPtr->rcList.left,
4332                infoPtr->rcList.bottom - infoPtr->rcList.top,
4333                hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4334
4335         DeleteObject(hbmp);
4336         DeleteDC(hdc);
4337     } else {
4338         SelectObject(hdc, hOldFont);
4339         SetBkMode(hdc, oldBkMode);
4340         SetBkColor(hdc, oldBkColor);
4341         SetTextColor(hdc, oldTextColor);
4342     }
4343
4344     infoPtr->bIsDrawing = FALSE;
4345 }
4346
4347
4348 /***
4349  * DESCRIPTION:
4350  * Calculates the approximate width and height of a given number of items.
4351  *
4352  * PARAMETER(S):
4353  * [I] infoPtr : valid pointer to the listview structure
4354  * [I] nItemCount : number of items
4355  * [I] wWidth : width
4356  * [I] wHeight : height
4357  *
4358  * RETURN:
4359  * Returns a DWORD. The width in the low word and the height in high word.
4360  */
4361 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4362                                             WORD wWidth, WORD wHeight)
4363 {
4364   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4365   INT nItemCountPerColumn = 1;
4366   INT nColumnCount = 0;
4367   DWORD dwViewRect = 0;
4368
4369   if (nItemCount == -1)
4370     nItemCount = infoPtr->nItemCount;
4371
4372   if (uView == LVS_LIST)
4373   {
4374     if (wHeight == 0xFFFF)
4375     {
4376       /* use current height */
4377       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4378     }
4379
4380     if (wHeight < infoPtr->nItemHeight)
4381       wHeight = infoPtr->nItemHeight;
4382
4383     if (nItemCount > 0)
4384     {
4385       if (infoPtr->nItemHeight > 0)
4386       {
4387         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4388         if (nItemCountPerColumn == 0)
4389           nItemCountPerColumn = 1;
4390
4391         if (nItemCount % nItemCountPerColumn != 0)
4392           nColumnCount = nItemCount / nItemCountPerColumn;
4393         else
4394           nColumnCount = nItemCount / nItemCountPerColumn + 1;
4395       }
4396     }
4397
4398     /* Microsoft padding magic */
4399     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4400     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4401
4402     dwViewRect = MAKELONG(wWidth, wHeight);
4403   }
4404   else if (uView == LVS_REPORT)
4405   {
4406     RECT rcBox;
4407
4408     if (infoPtr->nItemCount > 0)
4409     {
4410       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4411       wWidth = rcBox.right - rcBox.left;
4412       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4413     }
4414     else
4415     {
4416       /* use current height and width */
4417       if (wHeight == 0xffff)
4418           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4419       if (wWidth == 0xffff)
4420           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4421     }
4422
4423     dwViewRect = MAKELONG(wWidth, wHeight);
4424   }
4425   else if (uView == LVS_SMALLICON)
4426     FIXME("uView == LVS_SMALLICON: not implemented\n");
4427   else if (uView == LVS_ICON)
4428     FIXME("uView == LVS_ICON: not implemented\n");
4429
4430   return dwViewRect;
4431 }
4432
4433
4434 /***
4435  * DESCRIPTION:
4436  * Create a drag image list for the specified item.
4437  *
4438  * PARAMETER(S):
4439  * [I] infoPtr : valid pointer to the listview structure
4440  * [I] iItem   : index of item
4441  * [O] lppt    : Upperr-left corner of the image
4442  *
4443  * RETURN:
4444  * Returns a handle to the image list if successful, NULL otherwise.
4445  */
4446 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4447 {
4448     RECT rcItem;
4449     SIZE size;
4450     POINT pos;
4451     HDC hdc, hdcOrig;
4452     HBITMAP hbmp, hOldbmp;
4453     HIMAGELIST dragList = 0;
4454     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4455
4456     if (iItem < 0 || iItem >= infoPtr->nItemCount)
4457         return 0;
4458
4459     rcItem.left = LVIR_BOUNDS;
4460     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4461         return 0;
4462
4463     lppt->x = rcItem.left;
4464     lppt->y = rcItem.top;
4465
4466     size.cx = rcItem.right - rcItem.left;
4467     size.cy = rcItem.bottom - rcItem.top;
4468
4469     hdcOrig = GetDC(infoPtr->hwndSelf);
4470     hdc = CreateCompatibleDC(hdcOrig);
4471     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4472     hOldbmp = SelectObject(hdc, hbmp);
4473
4474     rcItem.left = rcItem.top = 0;
4475     rcItem.right = size.cx;
4476     rcItem.bottom = size.cy;
4477     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4478     
4479     pos.x = pos.y = 0;
4480     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4481     {
4482         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4483         SelectObject(hdc, hOldbmp);
4484         ImageList_Add(dragList, hbmp, 0);
4485     }
4486     else
4487         SelectObject(hdc, hOldbmp);
4488
4489     DeleteObject(hbmp);
4490     DeleteDC(hdc);
4491     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4492
4493     TRACE("ret=%p\n", dragList);
4494
4495     return dragList;
4496 }
4497
4498
4499 /***
4500  * DESCRIPTION:
4501  * Removes all listview items and subitems.
4502  *
4503  * PARAMETER(S):
4504  * [I] infoPtr : valid pointer to the listview structure
4505  *
4506  * RETURN:
4507  *   SUCCESS : TRUE
4508  *   FAILURE : FALSE
4509  */
4510 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4511 {
4512     NMLISTVIEW nmlv;
4513     HDPA hdpaSubItems = NULL;
4514     BOOL bSuppress;
4515     ITEMHDR *hdrItem;
4516     INT i, j;
4517
4518     TRACE("()\n");
4519
4520     /* we do it directly, to avoid notifications */
4521     ranges_clear(infoPtr->selectionRanges);
4522     infoPtr->nSelectionMark = -1;
4523     infoPtr->nFocusedItem = -1;
4524     SetRectEmpty(&infoPtr->rcFocus);
4525     /* But we are supposed to leave nHotItem as is! */
4526
4527
4528     /* send LVN_DELETEALLITEMS notification */
4529     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4530     nmlv.iItem = -1;
4531     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4532
4533     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4534     {
4535         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4536         {
4537             /* send LVN_DELETEITEM notification, if not suppressed
4538                and if it is not a virtual listview */
4539             if (!bSuppress) notify_deleteitem(infoPtr, i);
4540             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4541             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4542             {
4543                 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4544                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4545                 Free(hdrItem);
4546             }
4547             DPA_Destroy(hdpaSubItems);
4548             DPA_DeletePtr(infoPtr->hdpaItems, i);
4549         }
4550         DPA_DeletePtr(infoPtr->hdpaPosX, i);
4551         DPA_DeletePtr(infoPtr->hdpaPosY, i);
4552         infoPtr->nItemCount --;
4553     }
4554     
4555     if (!destroy)
4556     {
4557         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4558         LISTVIEW_UpdateScroll(infoPtr);
4559     }
4560     LISTVIEW_InvalidateList(infoPtr);
4561     
4562     return TRUE;
4563 }
4564
4565 /***
4566  * DESCRIPTION:
4567  * Scrolls, and updates the columns, when a column is changing width.
4568  *
4569  * PARAMETER(S):
4570  * [I] infoPtr : valid pointer to the listview structure
4571  * [I] nColumn : column to scroll
4572  * [I] dx : amount of scroll, in pixels
4573  *
4574  * RETURN:
4575  *   None.
4576  */
4577 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4578 {
4579     COLUMN_INFO *lpColumnInfo;
4580     RECT rcOld, rcCol;
4581     POINT ptOrigin;
4582     INT nCol;
4583    
4584     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4585     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4586     rcCol = lpColumnInfo->rcHeader;
4587     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4588         rcCol.left = rcCol.right;
4589     
4590     /* adjust the other columns */
4591     for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4592     {
4593         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4594         lpColumnInfo->rcHeader.left += dx;
4595         lpColumnInfo->rcHeader.right += dx;
4596     }
4597
4598     /* do not update screen if not in report mode */
4599     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4600     
4601     /* if we have a focus, we must first erase the focus rect */
4602     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4603     
4604     /* Need to reset the item width when inserting a new column */
4605     infoPtr->nItemWidth += dx;
4606
4607     LISTVIEW_UpdateScroll(infoPtr);
4608     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4609
4610     /* scroll to cover the deleted column, and invalidate for redraw */
4611     rcOld = infoPtr->rcList;
4612     rcOld.left = ptOrigin.x + rcCol.left + dx;
4613     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4614     
4615     /* we can restore focus now */
4616     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4617 }
4618
4619 /***
4620  * DESCRIPTION:
4621  * Removes a column from the listview control.
4622  *
4623  * PARAMETER(S):
4624  * [I] infoPtr : valid pointer to the listview structure
4625  * [I] nColumn : column index
4626  *
4627  * RETURN:
4628  *   SUCCESS : TRUE
4629  *   FAILURE : FALSE
4630  */
4631 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4632 {
4633     RECT rcCol;
4634     
4635     TRACE("nColumn=%d\n", nColumn);
4636
4637     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4638            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4639
4640     /* While the MSDN specifically says that column zero should not be deleted,
4641        what actually happens is that the column itself is deleted but no items or subitems
4642        are removed.
4643      */
4644
4645     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4646     
4647     if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4648         return FALSE;
4649
4650     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4651     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4652   
4653     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4654     {
4655         SUBITEM_INFO *lpSubItem, *lpDelItem;
4656         HDPA hdpaSubItems;
4657         INT nItem, nSubItem, i;
4658         
4659         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4660         {
4661             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4662             nSubItem = 0;
4663             lpDelItem = 0;
4664             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4665             {
4666                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4667                 if (lpSubItem->iSubItem == nColumn)
4668                 {
4669                     nSubItem = i;
4670                     lpDelItem = lpSubItem;
4671                 }
4672                 else if (lpSubItem->iSubItem > nColumn) 
4673                 {
4674                     lpSubItem->iSubItem--;
4675                 }
4676             }
4677
4678             /* if we found our subitem, zapp it */      
4679             if (nSubItem > 0)
4680             {
4681                 /* free string */
4682                 if (is_textW(lpDelItem->hdr.pszText))
4683                     Free(lpDelItem->hdr.pszText);
4684
4685                 /* free item */
4686                 Free(lpDelItem);
4687
4688                 /* free dpa memory */
4689                 DPA_DeletePtr(hdpaSubItems, nSubItem);
4690             }
4691         }
4692     }
4693
4694     /* update the other column info */
4695     LISTVIEW_UpdateItemSize(infoPtr);
4696     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4697         LISTVIEW_InvalidateList(infoPtr);
4698     else
4699         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4700
4701     return TRUE;
4702 }
4703
4704 /***
4705  * DESCRIPTION:
4706  * Invalidates the listview after an item's insertion or deletion.
4707  *
4708  * PARAMETER(S):
4709  * [I] infoPtr : valid pointer to the listview structure
4710  * [I] nItem : item index
4711  * [I] dir : -1 if deleting, 1 if inserting
4712  *
4713  * RETURN:
4714  *   None
4715  */
4716 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4717 {
4718     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4719     INT nPerCol, nItemCol, nItemRow;
4720     RECT rcScroll;
4721     POINT Origin;
4722
4723     /* if we don't refresh, what's the point of scrolling? */
4724     if (!is_redrawing(infoPtr)) return;
4725     
4726     assert (abs(dir) == 1);
4727
4728     /* arrange icons if autoarrange is on */
4729     if (is_autoarrange(infoPtr))
4730     {
4731         BOOL arrange = TRUE;
4732         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4733         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4734         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4735     }
4736
4737     /* scrollbars need updating */
4738     LISTVIEW_UpdateScroll(infoPtr);
4739
4740     /* figure out the item's position */ 
4741     if (uView == LVS_REPORT)
4742         nPerCol = infoPtr->nItemCount + 1;
4743     else if (uView == LVS_LIST)
4744         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4745     else /* LVS_ICON, or LVS_SMALLICON */
4746         return;
4747     
4748     nItemCol = nItem / nPerCol;
4749     nItemRow = nItem % nPerCol;
4750     LISTVIEW_GetOrigin(infoPtr, &Origin);
4751
4752     /* move the items below up a slot */
4753     rcScroll.left = nItemCol * infoPtr->nItemWidth;
4754     rcScroll.top = nItemRow * infoPtr->nItemHeight;
4755     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4756     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4757     OffsetRect(&rcScroll, Origin.x, Origin.y);
4758     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4759     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4760     {
4761         TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4762         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
4763                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4764     }
4765
4766     /* report has only that column, so we're done */
4767     if (uView == LVS_REPORT) return;
4768
4769     /* now for LISTs, we have to deal with the columns to the right */
4770     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4771     rcScroll.top = 0;
4772     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4773     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4774     OffsetRect(&rcScroll, Origin.x, Origin.y);
4775     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4776         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4777                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4778 }
4779
4780 /***
4781  * DESCRIPTION:
4782  * Removes an item from the listview control.
4783  *
4784  * PARAMETER(S):
4785  * [I] infoPtr : valid pointer to the listview structure
4786  * [I] nItem : item index
4787  *
4788  * RETURN:
4789  *   SUCCESS : TRUE
4790  *   FAILURE : FALSE
4791  */
4792 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4793 {
4794     LVITEMW item;
4795     const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4796     const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4797
4798     TRACE("(nItem=%d)\n", nItem);
4799
4800     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4801     
4802     /* remove selection, and focus */
4803     item.state = 0;
4804     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4805     LISTVIEW_SetItemState(infoPtr, nItem, &item);
4806             
4807     /* send LVN_DELETEITEM notification. */
4808     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4809
4810     /* we need to do this here, because we'll be deleting stuff */  
4811     if (is_icon)
4812         LISTVIEW_InvalidateItem(infoPtr, nItem);
4813     
4814     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4815     {
4816         HDPA hdpaSubItems;
4817         ITEMHDR *hdrItem;
4818         INT i;
4819
4820         hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4821         for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4822         {
4823             hdrItem = DPA_GetPtr(hdpaSubItems, i);
4824             if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4825             Free(hdrItem);
4826         }
4827         DPA_Destroy(hdpaSubItems);
4828     }
4829
4830     if (is_icon)
4831     {
4832         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4833         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4834     }
4835
4836     infoPtr->nItemCount--;
4837     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4838
4839     /* now is the invalidation fun */
4840     if (!is_icon)
4841         LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4842     return TRUE;
4843 }
4844
4845
4846 /***
4847  * DESCRIPTION:
4848  * Callback implementation for editlabel control
4849  *
4850  * PARAMETER(S):
4851  * [I] infoPtr : valid pointer to the listview structure
4852  * [I] pszText : modified text
4853  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4854  *
4855  * RETURN:
4856  *   SUCCESS : TRUE
4857  *   FAILURE : FALSE
4858  */
4859 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4860 {
4861     HWND hwndSelf = infoPtr->hwndSelf;
4862     NMLVDISPINFOW dispInfo;
4863     INT editedItem = infoPtr->nEditLabelItem;
4864     BOOL bSame;
4865
4866     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4867
4868     infoPtr->nEditLabelItem = -1;
4869
4870     ZeroMemory(&dispInfo, sizeof(dispInfo));
4871     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4872     dispInfo.item.iItem = editedItem;
4873     dispInfo.item.iSubItem = 0;
4874     dispInfo.item.stateMask = ~0;
4875     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4876
4877     if (isW)
4878         bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4879     else
4880     {
4881         LPWSTR tmp = textdupTtoW(pszText, FALSE);
4882         bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4883         textfreeT(tmp, FALSE);
4884     }
4885     if (bSame) return TRUE;
4886
4887     /* add the text from the edit in */
4888     dispInfo.item.mask |= LVIF_TEXT;
4889     dispInfo.item.pszText = pszText;
4890     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4891
4892     /* Do we need to update the Item Text */
4893     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4894     if (!IsWindow(hwndSelf))
4895         return FALSE;
4896     if (!pszText) return TRUE;
4897
4898     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4899     {
4900         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4901         ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4902         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4903         {
4904             LISTVIEW_InvalidateItem(infoPtr, editedItem);
4905             return TRUE;
4906         }
4907     }
4908
4909     ZeroMemory(&dispInfo, sizeof(dispInfo));
4910     dispInfo.item.mask = LVIF_TEXT;
4911     dispInfo.item.iItem = editedItem;
4912     dispInfo.item.iSubItem = 0;
4913     dispInfo.item.pszText = pszText;
4914     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4915     return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4916 }
4917
4918 /***
4919  * DESCRIPTION:
4920  * Begin in place editing of specified list view item
4921  *
4922  * PARAMETER(S):
4923  * [I] infoPtr : valid pointer to the listview structure
4924  * [I] nItem : item index
4925  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4926  *
4927  * RETURN:
4928  *   SUCCESS : TRUE
4929  *   FAILURE : FALSE
4930  */
4931 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4932 {
4933     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4934     NMLVDISPINFOW dispInfo;
4935     RECT rect;
4936     HWND hwndSelf = infoPtr->hwndSelf;
4937
4938     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4939
4940     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4941     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4942
4943     infoPtr->nEditLabelItem = nItem;
4944
4945     /* Is the EditBox still there, if so remove it */
4946     if(infoPtr->hwndEdit != 0)
4947     {
4948         SetFocus(infoPtr->hwndSelf);
4949         infoPtr->hwndEdit = 0;
4950     }
4951
4952     LISTVIEW_SetSelection(infoPtr, nItem);
4953     LISTVIEW_SetItemFocus(infoPtr, nItem);
4954     LISTVIEW_InvalidateItem(infoPtr, nItem);
4955
4956     rect.left = LVIR_LABEL;
4957     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4958     
4959     ZeroMemory(&dispInfo, sizeof(dispInfo));
4960     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4961     dispInfo.item.iItem = nItem;
4962     dispInfo.item.iSubItem = 0;
4963     dispInfo.item.stateMask = ~0;
4964     dispInfo.item.pszText = szDispText;
4965     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4966     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4967
4968     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4969                     rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4970     if (!infoPtr->hwndEdit) return 0;
4971     
4972     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4973     {
4974         if (!IsWindow(hwndSelf))
4975             return 0;
4976         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4977         infoPtr->hwndEdit = 0;
4978         return 0;
4979     }
4980
4981     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4982     SetFocus(infoPtr->hwndEdit);
4983     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4984     return infoPtr->hwndEdit;
4985 }
4986
4987
4988 /***
4989  * DESCRIPTION:
4990  * Ensures the specified item is visible, scrolling into view if necessary.
4991  *
4992  * PARAMETER(S):
4993  * [I] infoPtr : valid pointer to the listview structure
4994  * [I] nItem : item index
4995  * [I] bPartial : partially or entirely visible
4996  *
4997  * RETURN:
4998  *   SUCCESS : TRUE
4999  *   FAILURE : FALSE
5000  */
5001 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5002 {
5003     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5004     INT nScrollPosHeight = 0;
5005     INT nScrollPosWidth = 0;
5006     INT nHorzAdjust = 0;
5007     INT nVertAdjust = 0;
5008     INT nHorzDiff = 0;
5009     INT nVertDiff = 0;
5010     RECT rcItem, rcTemp;
5011
5012     rcItem.left = LVIR_BOUNDS;
5013     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5014
5015     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5016     
5017     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5018     {
5019         /* scroll left/right, but in LVS_REPORT mode */
5020         if (uView == LVS_LIST)
5021             nScrollPosWidth = infoPtr->nItemWidth;
5022         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5023             nScrollPosWidth = 1;
5024
5025         if (rcItem.left < infoPtr->rcList.left)
5026         {
5027             nHorzAdjust = -1;
5028             if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5029         }
5030         else
5031         {
5032             nHorzAdjust = 1;
5033             if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5034         }
5035     }
5036
5037     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5038     {
5039         /* scroll up/down, but not in LVS_LIST mode */
5040         if (uView == LVS_REPORT)
5041             nScrollPosHeight = infoPtr->nItemHeight;
5042         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5043             nScrollPosHeight = 1;
5044
5045         if (rcItem.top < infoPtr->rcList.top)
5046         {
5047             nVertAdjust = -1;
5048             if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5049         }
5050         else
5051         {
5052             nVertAdjust = 1;
5053             if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5054         }
5055     }
5056
5057     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5058
5059     if (nScrollPosWidth)
5060     {
5061         INT diff = nHorzDiff / nScrollPosWidth;
5062         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5063         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5064     }
5065
5066     if (nScrollPosHeight)
5067     {
5068         INT diff = nVertDiff / nScrollPosHeight;
5069         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5070         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5071     }
5072
5073     return TRUE;
5074 }
5075
5076 /***
5077  * DESCRIPTION:
5078  * Searches for an item with specific characteristics.
5079  *
5080  * PARAMETER(S):
5081  * [I] hwnd : window handle
5082  * [I] nStart : base item index
5083  * [I] lpFindInfo : item information to look for
5084  *
5085  * RETURN:
5086  *   SUCCESS : index of item
5087  *   FAILURE : -1
5088  */
5089 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5090                               const LVFINDINFOW *lpFindInfo)
5091 {
5092     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5093     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5094     BOOL bWrap = FALSE, bNearest = FALSE;
5095     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5096     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5097     POINT Position, Destination;
5098     LVITEMW lvItem;
5099
5100     /* Search in virtual listviews should be done by application, not by
5101        listview control, so we just send LVN_ODFINDITEMW and return the result */
5102     if (infoPtr->dwStyle & LVS_OWNERDATA)
5103     {
5104         NMLVFINDITEMW nmlv;
5105
5106         nmlv.iStart = nStart;
5107         nmlv.lvfi = *lpFindInfo;
5108         return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5109     }
5110
5111     if (!lpFindInfo || nItem < 0) return -1;
5112     
5113     lvItem.mask = 0;
5114     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5115     {
5116         lvItem.mask |= LVIF_TEXT;
5117         lvItem.pszText = szDispText;
5118         lvItem.cchTextMax = DISP_TEXT_SIZE;
5119     }
5120
5121     if (lpFindInfo->flags & LVFI_WRAP)
5122         bWrap = TRUE;
5123
5124     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
5125         (uView == LVS_ICON || uView ==LVS_SMALLICON))
5126     {
5127         POINT Origin;
5128         RECT rcArea;
5129         
5130         LISTVIEW_GetOrigin(infoPtr, &Origin);
5131         Destination.x = lpFindInfo->pt.x - Origin.x;
5132         Destination.y = lpFindInfo->pt.y - Origin.y;
5133         switch(lpFindInfo->vkDirection)
5134         {
5135         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
5136         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
5137         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5138         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
5139         case VK_HOME:  Destination.x = Destination.y = 0; break;
5140         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5141         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5142         case VK_END:
5143             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5144             Destination.x = rcArea.right; 
5145             Destination.y = rcArea.bottom; 
5146             break;
5147         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5148         }
5149         bNearest = TRUE;
5150     }
5151     else Destination.x = Destination.y = 0;
5152
5153     /* if LVFI_PARAM is specified, all other flags are ignored */
5154     if (lpFindInfo->flags & LVFI_PARAM)
5155     {
5156         lvItem.mask |= LVIF_PARAM;
5157         bNearest = FALSE;
5158         lvItem.mask &= ~LVIF_TEXT;
5159     }
5160
5161 again:
5162     for (; nItem < nLast; nItem++)
5163     {
5164         lvItem.iItem = nItem;
5165         lvItem.iSubItem = 0;
5166         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5167
5168         if (lvItem.mask & LVIF_PARAM)
5169         {
5170             if (lpFindInfo->lParam == lvItem.lParam)
5171                 return nItem;
5172             else
5173                 continue;
5174         }
5175         
5176         if (lvItem.mask & LVIF_TEXT)
5177         {
5178             if (lpFindInfo->flags & LVFI_PARTIAL)
5179             {
5180                 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5181             }
5182             else
5183             {
5184                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5185             }
5186         }
5187
5188         if (!bNearest) return nItem;
5189         
5190         /* This is very inefficient. To do a good job here,
5191          * we need a sorted array of (x,y) item positions */
5192         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5193
5194         /* compute the distance^2 to the destination */
5195         xdist = Destination.x - Position.x;
5196         ydist = Destination.y - Position.y;
5197         dist = xdist * xdist + ydist * ydist;
5198
5199         /* remember the distance, and item if it's closer */
5200         if (dist < mindist)
5201         {
5202             mindist = dist;
5203             nNearestItem = nItem;
5204         }
5205     }
5206
5207     if (bWrap)
5208     {
5209         nItem = 0;
5210         nLast = min(nStart + 1, infoPtr->nItemCount);
5211         bWrap = FALSE;
5212         goto again;
5213     }
5214
5215     return nNearestItem;
5216 }
5217
5218 /***
5219  * DESCRIPTION:
5220  * Searches for an item with specific characteristics.
5221  *
5222  * PARAMETER(S):
5223  * [I] hwnd : window handle
5224  * [I] nStart : base item index
5225  * [I] lpFindInfo : item information to look for
5226  *
5227  * RETURN:
5228  *   SUCCESS : index of item
5229  *   FAILURE : -1
5230  */
5231 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5232                               const LVFINDINFOA *lpFindInfo)
5233 {
5234     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5235     LVFINDINFOW fiw;
5236     INT res;
5237     LPWSTR strW = NULL;
5238
5239     memcpy(&fiw, lpFindInfo, sizeof(fiw));
5240     if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5241     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5242     textfreeT(strW, FALSE);
5243     return res;
5244 }
5245
5246 /***
5247  * DESCRIPTION:
5248  * Retrieves the background image of the listview control.
5249  *
5250  * PARAMETER(S):
5251  * [I] infoPtr : valid pointer to the listview structure
5252  * [O] lpBkImage : background image attributes
5253  *
5254  * RETURN:
5255  *   SUCCESS : TRUE
5256  *   FAILURE : FALSE
5257  */
5258 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
5259 /* {   */
5260 /*   FIXME (listview, "empty stub!\n"); */
5261 /*   return FALSE;   */
5262 /* }   */
5263
5264 /***
5265  * DESCRIPTION:
5266  * Retrieves column attributes.
5267  *
5268  * PARAMETER(S):
5269  * [I] infoPtr : valid pointer to the listview structure
5270  * [I] nColumn :  column index
5271  * [IO] lpColumn : column information
5272  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5273  *           otherwise it is in fact a LPLVCOLUMNA
5274  *
5275  * RETURN:
5276  *   SUCCESS : TRUE
5277  *   FAILURE : FALSE
5278  */
5279 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5280 {
5281     COLUMN_INFO *lpColumnInfo;
5282     HDITEMW hdi;
5283
5284     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5285     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5286
5287     /* initialize memory */
5288     ZeroMemory(&hdi, sizeof(hdi));
5289
5290     if (lpColumn->mask & LVCF_TEXT)
5291     {
5292         hdi.mask |= HDI_TEXT;
5293         hdi.pszText = lpColumn->pszText;
5294         hdi.cchTextMax = lpColumn->cchTextMax;
5295     }
5296
5297     if (lpColumn->mask & LVCF_IMAGE)
5298         hdi.mask |= HDI_IMAGE;
5299
5300     if (lpColumn->mask & LVCF_ORDER)
5301         hdi.mask |= HDI_ORDER;
5302
5303     if (lpColumn->mask & LVCF_SUBITEM)
5304         hdi.mask |= HDI_LPARAM;
5305
5306     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5307
5308     if (lpColumn->mask & LVCF_FMT)
5309         lpColumn->fmt = lpColumnInfo->fmt;
5310
5311     if (lpColumn->mask & LVCF_WIDTH)
5312         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5313
5314     if (lpColumn->mask & LVCF_IMAGE)
5315         lpColumn->iImage = hdi.iImage;
5316
5317     if (lpColumn->mask & LVCF_ORDER)
5318         lpColumn->iOrder = hdi.iOrder;
5319
5320     if (lpColumn->mask & LVCF_SUBITEM)
5321         lpColumn->iSubItem = hdi.lParam;
5322
5323     return TRUE;
5324 }
5325
5326
5327 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5328 {
5329     INT i;
5330
5331     if (!lpiArray)
5332         return FALSE;
5333
5334     /* FIXME: little hack */
5335     for (i = 0; i < iCount; i++)
5336         lpiArray[i] = i;
5337
5338     return TRUE;
5339 }
5340
5341 /***
5342  * DESCRIPTION:
5343  * Retrieves the column width.
5344  *
5345  * PARAMETER(S):
5346  * [I] infoPtr : valid pointer to the listview structure
5347  * [I] int : column index
5348  *
5349  * RETURN:
5350  *   SUCCESS : column width
5351  *   FAILURE : zero
5352  */
5353 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5354 {
5355     INT nColumnWidth = 0;
5356     HDITEMW hdItem;
5357
5358     TRACE("nColumn=%d\n", nColumn);
5359
5360     /* we have a 'column' in LIST and REPORT mode only */
5361     switch(infoPtr->dwStyle & LVS_TYPEMASK)
5362     {
5363     case LVS_LIST:
5364         nColumnWidth = infoPtr->nItemWidth;
5365         break;
5366     case LVS_REPORT:
5367         /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5368          * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5369          * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5370          *
5371          * TODO: should we do the same in LVM_GETCOLUMN?
5372          */
5373         hdItem.mask = HDI_WIDTH;
5374         if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5375         {
5376             WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5377             return 0;
5378         }
5379         nColumnWidth = hdItem.cxy;
5380         break;
5381     }
5382
5383     TRACE("nColumnWidth=%d\n", nColumnWidth);
5384     return nColumnWidth;
5385 }
5386
5387 /***
5388  * DESCRIPTION:
5389  * In list or report display mode, retrieves the number of items that can fit
5390  * vertically in the visible area. In icon or small icon display mode,
5391  * retrieves the total number of visible items.
5392  *
5393  * PARAMETER(S):
5394  * [I] infoPtr : valid pointer to the listview structure
5395  *
5396  * RETURN:
5397  * Number of fully visible items.
5398  */
5399 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5400 {
5401     switch (infoPtr->dwStyle & LVS_TYPEMASK)
5402     {
5403     case LVS_ICON:
5404     case LVS_SMALLICON:
5405         return infoPtr->nItemCount;
5406     case LVS_REPORT:
5407         return LISTVIEW_GetCountPerColumn(infoPtr);
5408     case LVS_LIST:
5409         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5410     }
5411     assert(FALSE);
5412     return 0;
5413 }
5414
5415 /***
5416  * DESCRIPTION:
5417  * Retrieves an image list handle.
5418  *
5419  * PARAMETER(S):
5420  * [I] infoPtr : valid pointer to the listview structure
5421  * [I] nImageList : image list identifier
5422  *
5423  * RETURN:
5424  *   SUCCESS : image list handle
5425  *   FAILURE : NULL
5426  */
5427 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5428 {
5429     switch (nImageList)
5430     {
5431     case LVSIL_NORMAL: return infoPtr->himlNormal;
5432     case LVSIL_SMALL: return infoPtr->himlSmall;
5433     case LVSIL_STATE: return infoPtr->himlState;
5434     }
5435     return NULL;
5436 }
5437
5438 /* LISTVIEW_GetISearchString */
5439
5440 /***
5441  * DESCRIPTION:
5442  * Retrieves item attributes.
5443  *
5444  * PARAMETER(S):
5445  * [I] hwnd : window handle
5446  * [IO] lpLVItem : item info
5447  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5448  *           if FALSE, then lpLVItem is a LPLVITEMA.
5449  *
5450  * NOTE:
5451  *   This is the internal 'GetItem' interface -- it tries to
5452  *   be smart and avoid text copies, if possible, by modifying
5453  *   lpLVItem->pszText to point to the text string. Please note
5454  *   that this is not always possible (e.g. OWNERDATA), so on
5455  *   entry you *must* supply valid values for pszText, and cchTextMax.
5456  *   The only difference to the documented interface is that upon
5457  *   return, you should use *only* the lpLVItem->pszText, rather than
5458  *   the buffer pointer you provided on input. Most code already does
5459  *   that, so it's not a problem.
5460  *   For the two cases when the text must be copied (that is,
5461  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5462  *
5463  * RETURN:
5464  *   SUCCESS : TRUE
5465  *   FAILURE : FALSE
5466  */
5467 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5468 {
5469     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5470     NMLVDISPINFOW dispInfo;
5471     ITEM_INFO *lpItem;
5472     ITEMHDR* pItemHdr;
5473     HDPA hdpaSubItems;
5474     INT isubitem;
5475
5476     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5477
5478     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5479         return FALSE;
5480
5481     if (lpLVItem->mask == 0) return TRUE;
5482
5483     /* make a local copy */
5484     isubitem = lpLVItem->iSubItem;
5485
5486     /* a quick optimization if all we're asked is the focus state
5487      * these queries are worth optimising since they are common,
5488      * and can be answered in constant time, without the heavy accesses */
5489     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5490          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5491     {
5492         lpLVItem->state = 0;
5493         if (infoPtr->nFocusedItem == lpLVItem->iItem)
5494             lpLVItem->state |= LVIS_FOCUSED;
5495         return TRUE;
5496     }
5497
5498     ZeroMemory(&dispInfo, sizeof(dispInfo));
5499
5500     /* if the app stores all the data, handle it separately */
5501     if (infoPtr->dwStyle & LVS_OWNERDATA)
5502     {
5503         dispInfo.item.state = 0;
5504
5505         /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5506         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5507         {
5508             /* NOTE: copy only fields which we _know_ are initialized, some apps
5509              *       depend on the uninitialized fields being 0 */
5510             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5511             dispInfo.item.iItem = lpLVItem->iItem;
5512             dispInfo.item.iSubItem = isubitem;
5513             if (lpLVItem->mask & LVIF_TEXT)
5514             {
5515                 dispInfo.item.pszText = lpLVItem->pszText;
5516                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;                
5517             }
5518             if (lpLVItem->mask & LVIF_STATE)
5519                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5520             notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5521             dispInfo.item.stateMask = lpLVItem->stateMask;
5522             if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5523             {
5524                 /* full size structure expected - _WIN32IE >= 0x560 */
5525                 *lpLVItem = dispInfo.item;
5526             }
5527             else if (lpLVItem->mask & LVIF_INDENT)
5528             {
5529                 /* indent member expected - _WIN32IE >= 0x300 */
5530                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5531             }
5532             else
5533             {
5534                 /* minimal structure expected */
5535                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5536             }
5537             TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5538         }
5539         
5540         /* make sure lParam is zeroed out */
5541         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5542         
5543         /* we store only a little state, so if we're not asked, we're done */
5544         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5545
5546         /* if focus is handled by us, report it */
5547         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5548         {
5549             lpLVItem->state &= ~LVIS_FOCUSED;
5550             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5551                 lpLVItem->state |= LVIS_FOCUSED;
5552         }
5553
5554         /* and do the same for selection, if we handle it */
5555         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5556         {
5557             lpLVItem->state &= ~LVIS_SELECTED;
5558             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5559                 lpLVItem->state |= LVIS_SELECTED;
5560         }
5561         
5562         return TRUE;
5563     }
5564
5565     /* find the item and subitem structures before we proceed */
5566     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5567     lpItem = DPA_GetPtr(hdpaSubItems, 0);
5568     assert (lpItem);
5569
5570     if (isubitem)
5571     {
5572         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5573         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5574         if (!lpSubItem)
5575         {
5576             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5577             isubitem = 0;
5578         }
5579     }
5580     else
5581         pItemHdr = &lpItem->hdr;
5582
5583     /* Do we need to query the state from the app? */
5584     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5585     {
5586         dispInfo.item.mask |= LVIF_STATE;
5587         dispInfo.item.stateMask = infoPtr->uCallbackMask;
5588     }
5589   
5590     /* Do we need to enquire about the image? */
5591     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5592         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5593     {
5594         dispInfo.item.mask |= LVIF_IMAGE;
5595         dispInfo.item.iImage = I_IMAGECALLBACK;
5596     }
5597
5598     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5599     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5600     {
5601         dispInfo.item.mask |= LVIF_TEXT;
5602         dispInfo.item.pszText = lpLVItem->pszText;
5603         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5604         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5605             *dispInfo.item.pszText = '\0';
5606     }
5607
5608     /* If we don't have all the requested info, query the application */
5609     if (dispInfo.item.mask != 0)
5610     {
5611         dispInfo.item.iItem = lpLVItem->iItem;
5612         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5613         dispInfo.item.lParam = lpItem->lParam;
5614         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5615         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5616     }
5617
5618     /* we should not store values for subitems */
5619     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5620
5621     /* Now, handle the iImage field */
5622     if (dispInfo.item.mask & LVIF_IMAGE)
5623     {
5624         lpLVItem->iImage = dispInfo.item.iImage;
5625         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5626             pItemHdr->iImage = dispInfo.item.iImage;
5627     }
5628     else if (lpLVItem->mask & LVIF_IMAGE)
5629     {
5630         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5631             lpLVItem->iImage = pItemHdr->iImage;
5632         else
5633             lpLVItem->iImage = 0;
5634     }
5635
5636     /* The pszText field */
5637     if (dispInfo.item.mask & LVIF_TEXT)
5638     {
5639         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5640             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5641
5642         lpLVItem->pszText = dispInfo.item.pszText;
5643     }
5644     else if (lpLVItem->mask & LVIF_TEXT)
5645     {
5646         if (isW) lpLVItem->pszText = pItemHdr->pszText;
5647         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5648     }
5649
5650     /* Next is the lParam field */
5651     if (dispInfo.item.mask & LVIF_PARAM)
5652     {
5653         lpLVItem->lParam = dispInfo.item.lParam;
5654         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5655             lpItem->lParam = dispInfo.item.lParam;
5656     }
5657     else if (lpLVItem->mask & LVIF_PARAM)
5658         lpLVItem->lParam = lpItem->lParam;
5659
5660     /* if this is a subitem, we're done */
5661     if (isubitem) return TRUE;
5662
5663     /* ... the state field (this one is different due to uCallbackmask) */
5664     if (lpLVItem->mask & LVIF_STATE)
5665     {
5666         lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5667         if (dispInfo.item.mask & LVIF_STATE)
5668         {
5669             lpLVItem->state &= ~dispInfo.item.stateMask;
5670             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5671         }
5672         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5673         {
5674             lpLVItem->state &= ~LVIS_FOCUSED;
5675             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5676                 lpLVItem->state |= LVIS_FOCUSED;
5677         }
5678         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5679         {
5680             lpLVItem->state &= ~LVIS_SELECTED;
5681             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5682                 lpLVItem->state |= LVIS_SELECTED;
5683         }           
5684     }
5685
5686     /* and last, but not least, the indent field */
5687     if (lpLVItem->mask & LVIF_INDENT)
5688         lpLVItem->iIndent = lpItem->iIndent;
5689
5690     return TRUE;
5691 }
5692
5693 /***
5694  * DESCRIPTION:
5695  * Retrieves item attributes.
5696  *
5697  * PARAMETER(S):
5698  * [I] hwnd : window handle
5699  * [IO] lpLVItem : item info
5700  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5701  *           if FALSE, then lpLVItem is a LPLVITEMA.
5702  *
5703  * NOTE:
5704  *   This is the external 'GetItem' interface -- it properly copies
5705  *   the text in the provided buffer.
5706  *
5707  * RETURN:
5708  *   SUCCESS : TRUE
5709  *   FAILURE : FALSE
5710  */
5711 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5712 {
5713     LPWSTR pszText;
5714     BOOL bResult;
5715
5716     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5717         return FALSE;
5718
5719     pszText = lpLVItem->pszText;
5720     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5721     if (bResult && lpLVItem->pszText != pszText)
5722         textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5723     lpLVItem->pszText = pszText;
5724
5725     return bResult;
5726 }
5727
5728
5729 /***
5730  * DESCRIPTION:
5731  * Retrieves the position (upper-left) of the listview control item.
5732  * Note that for LVS_ICON style, the upper-left is that of the icon
5733  * and not the bounding box.
5734  *
5735  * PARAMETER(S):
5736  * [I] infoPtr : valid pointer to the listview structure
5737  * [I] nItem : item index
5738  * [O] lpptPosition : coordinate information
5739  *
5740  * RETURN:
5741  *   SUCCESS : TRUE
5742  *   FAILURE : FALSE
5743  */
5744 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5745 {
5746     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5747     POINT Origin;
5748
5749     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5750
5751     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5752
5753     LISTVIEW_GetOrigin(infoPtr, &Origin);
5754     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5755
5756     if (uView == LVS_ICON)
5757     {
5758         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5759         lpptPosition->y += ICON_TOP_PADDING;
5760     }
5761     lpptPosition->x += Origin.x;
5762     lpptPosition->y += Origin.y;
5763     
5764     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5765     return TRUE;
5766 }
5767
5768
5769 /***
5770  * DESCRIPTION:
5771  * Retrieves the bounding rectangle for a listview control item.
5772  *
5773  * PARAMETER(S):
5774  * [I] infoPtr : valid pointer to the listview structure
5775  * [I] nItem : item index
5776  * [IO] lprc : bounding rectangle coordinates
5777  *     lprc->left specifies the portion of the item for which the bounding
5778  *     rectangle will be retrieved.
5779  *
5780  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5781  *        including the icon and label.
5782  *         *
5783  *         * For LVS_ICON
5784  *         * Experiment shows that native control returns:
5785  *         *  width = min (48, length of text line)
5786  *         *    .left = position.x - (width - iconsize.cx)/2
5787  *         *    .right = .left + width
5788  *         *  height = #lines of text * ntmHeight + icon height + 8
5789  *         *    .top = position.y - 2
5790  *         *    .bottom = .top + height
5791  *         *  separation between items .y = itemSpacing.cy - height
5792  *         *                           .x = itemSpacing.cx - width
5793  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5794  *         *
5795  *         * For LVS_ICON
5796  *         * Experiment shows that native control returns:
5797  *         *  width = iconSize.cx + 16
5798  *         *    .left = position.x - (width - iconsize.cx)/2
5799  *         *    .right = .left + width
5800  *         *  height = iconSize.cy + 4
5801  *         *    .top = position.y - 2
5802  *         *    .bottom = .top + height
5803  *         *  separation between items .y = itemSpacing.cy - height
5804  *         *                           .x = itemSpacing.cx - width
5805  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5806  *         *
5807  *         * For LVS_ICON
5808  *         * Experiment shows that native control returns:
5809  *         *  width = text length
5810  *         *    .left = position.x - width/2
5811  *         *    .right = .left + width
5812  *         *  height = ntmH * linecount + 2
5813  *         *    .top = position.y + iconSize.cy + 6
5814  *         *    .bottom = .top + height
5815  *         *  separation between items .y = itemSpacing.cy - height
5816  *         *                           .x = itemSpacing.cx - width
5817  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5818  *      rectangles, but excludes columns in report view.
5819  *
5820  * RETURN:
5821  *   SUCCESS : TRUE
5822  *   FAILURE : FALSE
5823  *
5824  * NOTES
5825  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5826  *   upon whether the window has the focus currently and on whether the item
5827  *   is the one with the focus.  Ensure that the control's record of which
5828  *   item has the focus agrees with the items' records.
5829  */
5830 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5831 {
5832     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5833     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5834     BOOL doLabel = TRUE, oversizedBox = FALSE;
5835     POINT Position, Origin;
5836     LVITEMW lvItem;
5837
5838     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5839
5840     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5841
5842     LISTVIEW_GetOrigin(infoPtr, &Origin);
5843     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5844
5845     /* Be smart and try to figure out the minimum we have to do */
5846     if (lprc->left == LVIR_ICON) doLabel = FALSE;
5847     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5848     if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5849         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5850         oversizedBox = TRUE;
5851
5852     /* get what we need from the item before hand, so we make
5853      * only one request. This can speed up things, if data
5854      * is stored on the app side */
5855     lvItem.mask = 0;
5856     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5857     if (doLabel) lvItem.mask |= LVIF_TEXT;
5858     lvItem.iItem = nItem;
5859     lvItem.iSubItem = 0;
5860     lvItem.pszText = szDispText;
5861     lvItem.cchTextMax = DISP_TEXT_SIZE;
5862     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5863     /* we got the state already up, simulate it here, to avoid a reget */
5864     if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5865     {
5866         lvItem.mask |= LVIF_STATE;
5867         lvItem.stateMask = LVIS_FOCUSED;
5868         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5869     }
5870
5871     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5872         lprc->left = LVIR_BOUNDS;
5873     switch(lprc->left)
5874     {
5875     case LVIR_ICON:
5876         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5877         break;
5878
5879     case LVIR_LABEL:
5880         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5881         break;
5882
5883     case LVIR_BOUNDS:
5884         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5885         break;
5886
5887     case LVIR_SELECTBOUNDS:
5888         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5889         break;
5890
5891     default:
5892         WARN("Unknown value: %d\n", lprc->left);
5893         return FALSE;
5894     }
5895
5896     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5897
5898     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5899
5900     return TRUE;
5901 }
5902
5903 /***
5904  * DESCRIPTION:
5905  * Retrieves the spacing between listview control items.
5906  *
5907  * PARAMETER(S):
5908  * [I] infoPtr : valid pointer to the listview structure
5909  * [IO] lprc : rectangle to receive the output
5910  *             on input, lprc->top = nSubItem
5911  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5912  * 
5913  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5914  *       not only those of the first column.
5915  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5916  * 
5917  * RETURN:
5918  *     TRUE: success
5919  *     FALSE: failure
5920  */
5921 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5922 {
5923     POINT Position;
5924     LVITEMW lvItem;
5925     INT nColumn;
5926     
5927     if (!lprc) return FALSE;
5928
5929     nColumn = lprc->top;
5930
5931     TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5932     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5933     if (lprc->top == 0)
5934         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5935
5936     if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5937
5938     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5939
5940     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5941
5942     lvItem.mask = 0;
5943     lvItem.iItem = nItem;
5944     lvItem.iSubItem = nColumn;
5945     
5946     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5947     switch(lprc->left)
5948     {
5949     case LVIR_ICON:
5950         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5951         break;
5952
5953     case LVIR_LABEL:
5954     case LVIR_BOUNDS:
5955         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5956         break;
5957
5958     default:
5959         ERR("Unknown bounds=%d\n", lprc->left);
5960         return FALSE;
5961     }
5962
5963     OffsetRect(lprc, Position.x, Position.y);
5964     return TRUE;
5965 }
5966
5967
5968 /***
5969  * DESCRIPTION:
5970  * Retrieves the width of a label.
5971  *
5972  * PARAMETER(S):
5973  * [I] infoPtr : valid pointer to the listview structure
5974  *
5975  * RETURN:
5976  *   SUCCESS : string width (in pixels)
5977  *   FAILURE : zero
5978  */
5979 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5980 {
5981     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5982     LVITEMW lvItem;
5983
5984     TRACE("(nItem=%d)\n", nItem);
5985
5986     lvItem.mask = LVIF_TEXT;
5987     lvItem.iItem = nItem;
5988     lvItem.iSubItem = 0;
5989     lvItem.pszText = szDispText;
5990     lvItem.cchTextMax = DISP_TEXT_SIZE;
5991     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5992   
5993     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5994 }
5995
5996 /***
5997  * DESCRIPTION:
5998  * Retrieves the spacing between listview control items.
5999  *
6000  * PARAMETER(S):
6001  * [I] infoPtr : valid pointer to the listview structure
6002  * [I] bSmall : flag for small or large icon
6003  *
6004  * RETURN:
6005  * Horizontal + vertical spacing
6006  */
6007 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6008 {
6009   LONG lResult;
6010
6011   if (!bSmall)
6012   {
6013     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6014   }
6015   else
6016   {
6017     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6018       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6019     else
6020       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6021   }
6022   return lResult;
6023 }
6024
6025 /***
6026  * DESCRIPTION:
6027  * Retrieves the state of a listview control item.
6028  *
6029  * PARAMETER(S):
6030  * [I] infoPtr : valid pointer to the listview structure
6031  * [I] nItem : item index
6032  * [I] uMask : state mask
6033  *
6034  * RETURN:
6035  * State specified by the mask.
6036  */
6037 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6038 {
6039     LVITEMW lvItem;
6040
6041     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6042
6043     lvItem.iItem = nItem;
6044     lvItem.iSubItem = 0;
6045     lvItem.mask = LVIF_STATE;
6046     lvItem.stateMask = uMask;
6047     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6048
6049     return lvItem.state & uMask;
6050 }
6051
6052 /***
6053  * DESCRIPTION:
6054  * Retrieves the text of a listview control item or subitem.
6055  *
6056  * PARAMETER(S):
6057  * [I] hwnd : window handle
6058  * [I] nItem : item index
6059  * [IO] lpLVItem : item information
6060  * [I] isW :  TRUE if lpLVItem is Unicode
6061  *
6062  * RETURN:
6063  *   SUCCESS : string length
6064  *   FAILURE : 0
6065  */
6066 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6067 {
6068     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6069
6070     lpLVItem->mask = LVIF_TEXT;
6071     lpLVItem->iItem = nItem;
6072     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6073
6074     return textlenT(lpLVItem->pszText, isW);
6075 }
6076
6077 /***
6078  * DESCRIPTION:
6079  * Searches for an item based on properties + relationships.
6080  *
6081  * PARAMETER(S):
6082  * [I] infoPtr : valid pointer to the listview structure
6083  * [I] nItem : item index
6084  * [I] uFlags : relationship flag
6085  *
6086  * RETURN:
6087  *   SUCCESS : item index
6088  *   FAILURE : -1
6089  */
6090 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6091 {
6092     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6093     UINT uMask = 0;
6094     LVFINDINFOW lvFindInfo;
6095     INT nCountPerColumn;
6096     INT nCountPerRow;
6097     INT i;
6098
6099     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6100     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6101
6102     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6103
6104     if (uFlags & LVNI_CUT)
6105       uMask |= LVIS_CUT;
6106
6107     if (uFlags & LVNI_DROPHILITED)
6108       uMask |= LVIS_DROPHILITED;
6109
6110     if (uFlags & LVNI_FOCUSED)
6111       uMask |= LVIS_FOCUSED;
6112
6113     if (uFlags & LVNI_SELECTED)
6114       uMask |= LVIS_SELECTED;
6115
6116     /* if we're asked for the focused item, that's only one, 
6117      * so it's worth optimizing */
6118     if (uFlags & LVNI_FOCUSED)
6119     {
6120         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6121         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6122     }
6123     
6124     if (uFlags & LVNI_ABOVE)
6125     {
6126       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6127       {
6128         while (nItem >= 0)
6129         {
6130           nItem--;
6131           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6132             return nItem;
6133         }
6134       }
6135       else
6136       {
6137         /* Special case for autoarrange - move 'til the top of a list */
6138         if (is_autoarrange(infoPtr))
6139         {
6140           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6141           while (nItem - nCountPerRow >= 0)
6142           {
6143             nItem -= nCountPerRow;
6144             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6145               return nItem;
6146           }
6147           return -1;
6148         }
6149         lvFindInfo.flags = LVFI_NEARESTXY;
6150         lvFindInfo.vkDirection = VK_UP;
6151         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6152         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6153         {
6154           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6155             return nItem;
6156         }
6157       }
6158     }
6159     else if (uFlags & LVNI_BELOW)
6160     {
6161       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6162       {
6163         while (nItem < infoPtr->nItemCount)
6164         {
6165           nItem++;
6166           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6167             return nItem;
6168         }
6169       }
6170       else
6171       {
6172         /* Special case for autoarrange - move 'til the bottom of a list */
6173         if (is_autoarrange(infoPtr))
6174         {
6175           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6176           while (nItem + nCountPerRow < infoPtr->nItemCount )
6177           {
6178             nItem += nCountPerRow;
6179             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6180               return nItem;
6181           }
6182           return -1;
6183         }
6184         lvFindInfo.flags = LVFI_NEARESTXY;
6185         lvFindInfo.vkDirection = VK_DOWN;
6186         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6187         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6188         {
6189           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6190             return nItem;
6191         }
6192       }
6193     }
6194     else if (uFlags & LVNI_TOLEFT)
6195     {
6196       if (uView == LVS_LIST)
6197       {
6198         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6199         while (nItem - nCountPerColumn >= 0)
6200         {
6201           nItem -= nCountPerColumn;
6202           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6203             return nItem;
6204         }
6205       }
6206       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6207       {
6208         /* Special case for autoarrange - move 'ti the beginning of a row */
6209         if (is_autoarrange(infoPtr))
6210         {
6211           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6212           while (nItem % nCountPerRow > 0)
6213           {
6214             nItem --;
6215             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6216               return nItem;
6217           }
6218           return -1;
6219         }
6220         lvFindInfo.flags = LVFI_NEARESTXY;
6221         lvFindInfo.vkDirection = VK_LEFT;
6222         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6223         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6224         {
6225           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6226             return nItem;
6227         }
6228       }
6229     }
6230     else if (uFlags & LVNI_TORIGHT)
6231     {
6232       if (uView == LVS_LIST)
6233       {
6234         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6235         while (nItem + nCountPerColumn < infoPtr->nItemCount)
6236         {
6237           nItem += nCountPerColumn;
6238           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6239             return nItem;
6240         }
6241       }
6242       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6243       {
6244         /* Special case for autoarrange - move 'til the end of a row */
6245         if (is_autoarrange(infoPtr))
6246         {
6247           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6248           while (nItem % nCountPerRow < nCountPerRow - 1 )
6249           {
6250             nItem ++;
6251             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6252               return nItem;
6253           }
6254           return -1;
6255         }
6256         lvFindInfo.flags = LVFI_NEARESTXY;
6257         lvFindInfo.vkDirection = VK_RIGHT;
6258         SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6259         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6260         {
6261           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6262             return nItem;
6263         }
6264       }
6265     }
6266     else
6267     {
6268       nItem++;
6269
6270       /* search by index */
6271       for (i = nItem; i < infoPtr->nItemCount; i++)
6272       {
6273         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6274           return i;
6275       }
6276     }
6277
6278     return -1;
6279 }
6280
6281 /* LISTVIEW_GetNumberOfWorkAreas */
6282
6283 /***
6284  * DESCRIPTION:
6285  * Retrieves the origin coordinates when in icon or small icon display mode.
6286  *
6287  * PARAMETER(S):
6288  * [I] infoPtr : valid pointer to the listview structure
6289  * [O] lpptOrigin : coordinate information
6290  *
6291  * RETURN:
6292  *   None.
6293  */
6294 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6295 {
6296     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6297     INT nHorzPos = 0, nVertPos = 0;
6298     SCROLLINFO scrollInfo;
6299
6300     scrollInfo.cbSize = sizeof(SCROLLINFO);    
6301     scrollInfo.fMask = SIF_POS;
6302     
6303     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6304         nHorzPos = scrollInfo.nPos;
6305     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6306         nVertPos = scrollInfo.nPos;
6307
6308     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6309
6310     lpptOrigin->x = infoPtr->rcList.left;
6311     lpptOrigin->y = infoPtr->rcList.top;
6312     if (uView == LVS_LIST)
6313         nHorzPos *= infoPtr->nItemWidth;
6314     else if (uView == LVS_REPORT)
6315         nVertPos *= infoPtr->nItemHeight;
6316     
6317     lpptOrigin->x -= nHorzPos;
6318     lpptOrigin->y -= nVertPos;
6319
6320     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6321 }
6322
6323 /***
6324  * DESCRIPTION:
6325  * Retrieves the width of a string.
6326  *
6327  * PARAMETER(S):
6328  * [I] hwnd : window handle
6329  * [I] lpszText : text string to process
6330  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6331  *
6332  * RETURN:
6333  *   SUCCESS : string width (in pixels)
6334  *   FAILURE : zero
6335  */
6336 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6337 {
6338     SIZE stringSize;
6339     
6340     stringSize.cx = 0;    
6341     if (is_textT(lpszText, isW))
6342     {
6343         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6344         HDC hdc = GetDC(infoPtr->hwndSelf);
6345         HFONT hOldFont = SelectObject(hdc, hFont);
6346
6347         if (isW)
6348             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6349         else
6350             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6351         SelectObject(hdc, hOldFont);
6352         ReleaseDC(infoPtr->hwndSelf, hdc);
6353     }
6354     return stringSize.cx;
6355 }
6356
6357 /***
6358  * DESCRIPTION:
6359  * Determines which listview item is located at the specified position.
6360  *
6361  * PARAMETER(S):
6362  * [I] infoPtr : valid pointer to the listview structure
6363  * [IO] lpht : hit test information
6364  * [I] subitem : fill out iSubItem.
6365  * [I] select : return the index only if the hit selects the item
6366  *
6367  * NOTE:
6368  * (mm 20001022): We must not allow iSubItem to be touched, for
6369  * an app might pass only a structure with space up to iItem!
6370  * (MS Office 97 does that for instance in the file open dialog)
6371  * 
6372  * RETURN:
6373  *   SUCCESS : item index
6374  *   FAILURE : -1
6375  */
6376 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6377 {
6378     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6379     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6380     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6381     POINT Origin, Position, opt;
6382     LVITEMW lvItem;
6383     ITERATOR i;
6384     INT iItem;
6385     
6386     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6387     
6388     lpht->flags = 0;
6389     lpht->iItem = -1;
6390     if (subitem) lpht->iSubItem = 0;
6391
6392     if (infoPtr->rcList.left > lpht->pt.x)
6393         lpht->flags |= LVHT_TOLEFT;
6394     else if (infoPtr->rcList.right < lpht->pt.x)
6395         lpht->flags |= LVHT_TORIGHT;
6396     
6397     if (infoPtr->rcList.top > lpht->pt.y)
6398         lpht->flags |= LVHT_ABOVE;
6399     else if (infoPtr->rcList.bottom < lpht->pt.y)
6400         lpht->flags |= LVHT_BELOW;
6401
6402     TRACE("lpht->flags=0x%x\n", lpht->flags);
6403     if (lpht->flags) return -1;
6404
6405     lpht->flags |= LVHT_NOWHERE;
6406
6407     LISTVIEW_GetOrigin(infoPtr, &Origin);
6408    
6409     /* first deal with the large items */
6410     rcSearch.left = lpht->pt.x;
6411     rcSearch.top = lpht->pt.y;
6412     rcSearch.right = rcSearch.left + 1;
6413     rcSearch.bottom = rcSearch.top + 1;
6414     
6415     iterator_frameditems(&i, infoPtr, &rcSearch);
6416     iterator_next(&i); /* go to first item in the sequence */
6417     iItem = i.nItem;
6418     iterator_destroy(&i);
6419    
6420     TRACE("lpht->iItem=%d\n", iItem); 
6421     if (iItem == -1) return -1;
6422
6423     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6424     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6425     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6426     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6427     lvItem.iItem = iItem;
6428     lvItem.iSubItem = 0;
6429     lvItem.pszText = szDispText;
6430     lvItem.cchTextMax = DISP_TEXT_SIZE;
6431     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6432     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6433
6434     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6435     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6436     opt.x = lpht->pt.x - Position.x - Origin.x;
6437     opt.y = lpht->pt.y - Position.y - Origin.y;
6438     
6439     if (uView == LVS_REPORT)
6440         rcBounds = rcBox;
6441     else
6442     {
6443         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6444         UnionRect(&rcBounds, &rcBounds, &rcState);
6445     }
6446     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6447     if (!PtInRect(&rcBounds, opt)) return -1;
6448
6449     if (PtInRect(&rcIcon, opt))
6450         lpht->flags |= LVHT_ONITEMICON;
6451     else if (PtInRect(&rcLabel, opt))
6452         lpht->flags |= LVHT_ONITEMLABEL;
6453     else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6454         lpht->flags |= LVHT_ONITEMSTATEICON;
6455     if (lpht->flags & LVHT_ONITEM)
6456         lpht->flags &= ~LVHT_NOWHERE;
6457    
6458     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6459     if (uView == LVS_REPORT && subitem)
6460     {
6461         INT j;
6462
6463         rcBounds.right = rcBounds.left;
6464         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6465         {
6466             rcBounds.left = rcBounds.right;
6467             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6468             if (PtInRect(&rcBounds, opt))
6469             {
6470                 lpht->iSubItem = j;
6471                 break;
6472             }
6473         }
6474     }
6475
6476     if (select && !(uView == LVS_REPORT &&
6477                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6478                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6479     {
6480         if (uView == LVS_REPORT)
6481         {
6482             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6483             UnionRect(&rcBounds, &rcBounds, &rcState);
6484         }
6485         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6486     }
6487     return lpht->iItem = iItem;
6488 }
6489
6490
6491 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6492    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6493    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6494    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6495    their own sort proc. when sending LVM_SORTITEMS.
6496 */
6497 /* Platform SDK:
6498     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6499         if:
6500           LVS_SORTXXX must be specified,
6501           LVS_OWNERDRAW is not set,
6502           <item>.pszText is not LPSTR_TEXTCALLBACK.
6503
6504     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6505     are sorted based on item text..."
6506 */
6507 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6508 {
6509     ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6510     ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6511     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
6512
6513     /* if we're sorting descending, negate the return value */
6514     return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6515 }
6516
6517 /***
6518  * DESCRIPTION:
6519  * Inserts a new item in the listview control.
6520  *
6521  * PARAMETER(S):
6522  * [I] infoPtr : valid pointer to the listview structure
6523  * [I] lpLVItem : item information
6524  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6525  *
6526  * RETURN:
6527  *   SUCCESS : new item index
6528  *   FAILURE : -1
6529  */
6530 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6531 {
6532     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6533     INT nItem;
6534     HDPA hdpaSubItems;
6535     NMLISTVIEW nmlv;
6536     ITEM_INFO *lpItem;
6537     BOOL is_sorted, has_changed;
6538     LVITEMW item;
6539     HWND hwndSelf = infoPtr->hwndSelf;
6540
6541     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6542
6543     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6544
6545     /* make sure it's an item, and not a subitem; cannot insert a subitem */
6546     if (!lpLVItem || lpLVItem->iSubItem) return -1;
6547
6548     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6549
6550     if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6551     
6552     /* insert item in listview control data structure */
6553     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6554     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6555
6556     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6557                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6558
6559     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6560
6561     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6562     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6563     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6564     if (nItem == -1) goto fail;
6565     infoPtr->nItemCount++;
6566
6567     /* shift indices first so they don't get tangled */
6568     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6569
6570     /* set the item attributes */
6571     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6572     {
6573         /* full size structure expected - _WIN32IE >= 0x560 */
6574         item = *lpLVItem;
6575     }
6576     else if (lpLVItem->mask & LVIF_INDENT)
6577     {
6578         /* indent member expected - _WIN32IE >= 0x300 */
6579         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6580     }
6581     else
6582     {
6583         /* minimal structure expected */
6584         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6585     }
6586     item.iItem = nItem;
6587     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6588     {
6589         item.mask |= LVIF_STATE;
6590         item.stateMask |= LVIS_STATEIMAGEMASK;
6591         item.state &= ~LVIS_STATEIMAGEMASK;
6592         item.state |= INDEXTOSTATEIMAGEMASK(1);
6593     }
6594     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6595
6596     /* if we're sorted, sort the list, and update the index */
6597     if (is_sorted)
6598     {
6599         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6600         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6601         assert(nItem != -1);
6602     }
6603
6604     /* make room for the position, if we are in the right mode */
6605     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6606     {
6607         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6608             goto undo;
6609         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6610         {
6611             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6612             goto undo;
6613         }
6614     }
6615     
6616     /* send LVN_INSERTITEM notification */
6617     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6618     nmlv.iItem = nItem;
6619     nmlv.lParam = lpItem->lParam;
6620     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6621     if (!IsWindow(hwndSelf))
6622         return -1;
6623
6624     /* align items (set position of each item) */
6625     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6626     {
6627         POINT pt;
6628
6629         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6630             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6631         else
6632             LISTVIEW_NextIconPosTop(infoPtr, &pt);
6633
6634         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6635     }
6636
6637     /* now is the invalidation fun */
6638     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6639     return nItem;
6640
6641 undo:
6642     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6643     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6644     infoPtr->nItemCount--;
6645 fail:
6646     DPA_DeletePtr(hdpaSubItems, 0);
6647     DPA_Destroy (hdpaSubItems);
6648     Free (lpItem);
6649     return -1;
6650 }
6651
6652 /***
6653  * DESCRIPTION:
6654  * Redraws a range of items.
6655  *
6656  * PARAMETER(S):
6657  * [I] infoPtr : valid pointer to the listview structure
6658  * [I] nFirst : first item
6659  * [I] nLast : last item
6660  *
6661  * RETURN:
6662  *   SUCCESS : TRUE
6663  *   FAILURE : FALSE
6664  */
6665 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6666 {
6667     INT i;
6668  
6669     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
6670         max(nFirst, nLast) >= infoPtr->nItemCount)
6671         return FALSE;
6672     
6673     for (i = nFirst; i <= nLast; i++)
6674         LISTVIEW_InvalidateItem(infoPtr, i);
6675
6676     return TRUE;
6677 }
6678
6679 /***
6680  * DESCRIPTION:
6681  * Scroll the content of a listview.
6682  *
6683  * PARAMETER(S):
6684  * [I] infoPtr : valid pointer to the listview structure
6685  * [I] dx : horizontal scroll amount in pixels
6686  * [I] dy : vertical scroll amount in pixels
6687  *
6688  * RETURN:
6689  *   SUCCESS : TRUE
6690  *   FAILURE : FALSE
6691  *
6692  * COMMENTS:
6693  *  If the control is in report mode (LVS_REPORT) the control can
6694  *  be scrolled only in line increments. "dy" will be rounded to the
6695  *  nearest number of pixels that are a whole line. Ex: if line height
6696  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6697  *  is passed, then the scroll will be 0.  (per MSDN 7/2002)
6698  *
6699  *  For:  (per experimentation with native control and CSpy ListView)
6700  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
6701  *                    dx ignored
6702  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
6703  *                    dx ignored
6704  *     LVS_LIST       dx=1 = 1 column (horizontal only)
6705  *                           but will only scroll 1 column per message
6706  *                           no matter what the value.
6707  *                    dy must be 0 or FALSE returned.
6708  *     LVS_REPORT     dx=1 = 1 pixel
6709  *                    dy=  see above
6710  *
6711  */
6712 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6713 {
6714     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6715     case LVS_REPORT:
6716         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6717         dy /= infoPtr->nItemHeight;
6718         break;
6719     case LVS_LIST:
6720         if (dy != 0) return FALSE;
6721         break;
6722     default: /* icon */
6723         dx = 0;
6724         break;
6725     }   
6726
6727     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6728     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6729   
6730     return TRUE;
6731 }
6732
6733 /***
6734  * DESCRIPTION:
6735  * Sets the background color.
6736  *
6737  * PARAMETER(S):
6738  * [I] infoPtr : valid pointer to the listview structure
6739  * [I] clrBk : background color
6740  *
6741  * RETURN:
6742  *   SUCCESS : TRUE
6743  *   FAILURE : FALSE
6744  */
6745 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6746 {
6747     TRACE("(clrBk=%x)\n", clrBk);
6748
6749     if(infoPtr->clrBk != clrBk) {
6750         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6751         infoPtr->clrBk = clrBk;
6752         if (clrBk == CLR_NONE)
6753             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6754         else
6755             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6756         LISTVIEW_InvalidateList(infoPtr);
6757     }
6758
6759    return TRUE;
6760 }
6761
6762 /* LISTVIEW_SetBkImage */
6763
6764 /*** Helper for {Insert,Set}ColumnT *only* */
6765 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6766                                const LVCOLUMNW *lpColumn, BOOL isW)
6767 {
6768     if (lpColumn->mask & LVCF_FMT)
6769     {
6770         /* format member is valid */
6771         lphdi->mask |= HDI_FORMAT;
6772
6773         /* set text alignment (leftmost column must be left-aligned) */
6774         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6775             lphdi->fmt |= HDF_LEFT;
6776         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6777             lphdi->fmt |= HDF_RIGHT;
6778         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6779             lphdi->fmt |= HDF_CENTER;
6780
6781         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6782             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6783
6784         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6785         {
6786             lphdi->fmt |= HDF_IMAGE;
6787             lphdi->iImage = I_IMAGECALLBACK;
6788         }
6789     }
6790
6791     if (lpColumn->mask & LVCF_WIDTH)
6792     {
6793         lphdi->mask |= HDI_WIDTH;
6794         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6795         {
6796             /* make it fill the remainder of the controls width */
6797             RECT rcHeader;
6798             INT item_index;
6799
6800             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6801             {
6802                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6803                 lphdi->cxy += rcHeader.right - rcHeader.left;
6804             }
6805
6806             /* retrieve the layout of the header */
6807             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6808             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6809
6810             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6811         }
6812         else
6813             lphdi->cxy = lpColumn->cx;
6814     }
6815
6816     if (lpColumn->mask & LVCF_TEXT)
6817     {
6818         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6819         lphdi->fmt |= HDF_STRING;
6820         lphdi->pszText = lpColumn->pszText;
6821         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6822     }
6823
6824     if (lpColumn->mask & LVCF_IMAGE)
6825     {
6826         lphdi->mask |= HDI_IMAGE;
6827         lphdi->iImage = lpColumn->iImage;
6828     }
6829
6830     if (lpColumn->mask & LVCF_ORDER)
6831     {
6832         lphdi->mask |= HDI_ORDER;
6833         lphdi->iOrder = lpColumn->iOrder;
6834     }
6835 }
6836
6837
6838 /***
6839  * DESCRIPTION:
6840  * Inserts a new column.
6841  *
6842  * PARAMETER(S):
6843  * [I] infoPtr : valid pointer to the listview structure
6844  * [I] nColumn : column index
6845  * [I] lpColumn : column information
6846  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6847  *
6848  * RETURN:
6849  *   SUCCESS : new column index
6850  *   FAILURE : -1
6851  */
6852 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6853                                   const LVCOLUMNW *lpColumn, BOOL isW)
6854 {
6855     COLUMN_INFO *lpColumnInfo;
6856     INT nNewColumn;
6857     HDITEMW hdi;
6858
6859     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6860
6861     if (!lpColumn || nColumn < 0) return -1;
6862     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6863     
6864     ZeroMemory(&hdi, sizeof(HDITEMW));
6865     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6866
6867     /*
6868      * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6869      * (can be seen in SPY) otherwise column never gets added.
6870      */
6871     if (!(lpColumn->mask & LVCF_WIDTH)) {
6872         hdi.mask |= HDI_WIDTH;
6873         hdi.cxy = 10;
6874     }
6875
6876     /*
6877      * when the iSubItem is available Windows copies it to the header lParam. It seems
6878      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6879      */
6880     if (lpColumn->mask & LVCF_SUBITEM)
6881     {
6882         hdi.mask |= HDI_LPARAM;
6883         hdi.lParam = lpColumn->iSubItem;
6884     }
6885
6886     /* insert item in header control */
6887     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6888                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6889                               (WPARAM)nColumn, (LPARAM)&hdi);
6890     if (nNewColumn == -1) return -1;
6891     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6892    
6893     /* create our own column info */ 
6894     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6895     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6896
6897     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6898     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6899
6900     /* now we have to actually adjust the data */
6901     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6902     {
6903         SUBITEM_INFO *lpSubItem;
6904         HDPA hdpaSubItems;
6905         INT nItem, i;
6906         
6907         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6908         {
6909             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
6910             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6911             {
6912                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
6913                 if (lpSubItem->iSubItem >= nNewColumn)
6914                     lpSubItem->iSubItem++;
6915             }
6916         }
6917     }
6918
6919     /* make space for the new column */
6920     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6921     LISTVIEW_UpdateItemSize(infoPtr);
6922     
6923     return nNewColumn;
6924
6925 fail:
6926     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6927     if (lpColumnInfo)
6928     {
6929         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6930         Free(lpColumnInfo);
6931     }
6932     return -1;
6933 }
6934
6935 /***
6936  * DESCRIPTION:
6937  * Sets the attributes of a header item.
6938  *
6939  * PARAMETER(S):
6940  * [I] infoPtr : valid pointer to the listview structure
6941  * [I] nColumn : column index
6942  * [I] lpColumn : column attributes
6943  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6944  *
6945  * RETURN:
6946  *   SUCCESS : TRUE
6947  *   FAILURE : FALSE
6948  */
6949 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6950                                 const LVCOLUMNW *lpColumn, BOOL isW)
6951 {
6952     HDITEMW hdi, hdiget;
6953     BOOL bResult;
6954
6955     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6956     
6957     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6958
6959     ZeroMemory(&hdi, sizeof(HDITEMW));
6960     if (lpColumn->mask & LVCF_FMT)
6961     {
6962         hdi.mask |= HDI_FORMAT;
6963         hdiget.mask = HDI_FORMAT;
6964         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6965             hdi.fmt = hdiget.fmt & HDF_STRING;
6966     }
6967     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6968
6969     /* set header item attributes */
6970     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6971     if (!bResult) return FALSE;
6972
6973     if (lpColumn->mask & LVCF_FMT)
6974     {
6975         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6976         int oldFmt = lpColumnInfo->fmt;
6977         
6978         lpColumnInfo->fmt = lpColumn->fmt;
6979         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6980         {
6981             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6982             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6983         }
6984     }
6985
6986     return TRUE;
6987 }
6988
6989 /***
6990  * DESCRIPTION:
6991  * Sets the column order array
6992  *
6993  * PARAMETERS:
6994  * [I] infoPtr : valid pointer to the listview structure
6995  * [I] iCount : number of elements in column order array
6996  * [I] lpiArray : pointer to column order array
6997  *
6998  * RETURN:
6999  *   SUCCESS : TRUE
7000  *   FAILURE : FALSE
7001  */
7002 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7003 {
7004   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7005
7006   if (!lpiArray)
7007     return FALSE;
7008
7009   return TRUE;
7010 }
7011
7012
7013 /***
7014  * DESCRIPTION:
7015  * Sets the width of a column
7016  *
7017  * PARAMETERS:
7018  * [I] infoPtr : valid pointer to the listview structure
7019  * [I] nColumn : column index
7020  * [I] cx : column width
7021  *
7022  * RETURN:
7023  *   SUCCESS : TRUE
7024  *   FAILURE : FALSE
7025  */
7026 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7027 {
7028     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7029     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7030     INT max_cx = 0;
7031     HDITEMW hdi;
7032
7033     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7034
7035     /* set column width only if in report or list mode */
7036     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7037
7038     /* take care of invalid cx values */
7039     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7040     else if (uView == LVS_LIST && cx < 1) return FALSE;
7041
7042     /* resize all columns if in LVS_LIST mode */
7043     if(uView == LVS_LIST) 
7044     {
7045         infoPtr->nItemWidth = cx;
7046         LISTVIEW_InvalidateList(infoPtr);
7047         return TRUE;
7048     }
7049
7050     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7051
7052     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7053     {
7054         INT nLabelWidth;
7055         LVITEMW lvItem;
7056
7057         lvItem.mask = LVIF_TEXT;        
7058         lvItem.iItem = 0;
7059         lvItem.iSubItem = nColumn;
7060         lvItem.pszText = szDispText;
7061         lvItem.cchTextMax = DISP_TEXT_SIZE;
7062         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7063         {
7064             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7065             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7066             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7067         }
7068         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7069             max_cx += infoPtr->iconSize.cx;
7070         max_cx += TRAILING_LABEL_PADDING;
7071     }
7072
7073     /* autosize based on listview items width */
7074     if(cx == LVSCW_AUTOSIZE)
7075         cx = max_cx;
7076     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7077     {
7078         /* if iCol is the last column make it fill the remainder of the controls width */
7079         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
7080         {
7081             RECT rcHeader;
7082             POINT Origin;
7083
7084             LISTVIEW_GetOrigin(infoPtr, &Origin);
7085             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7086
7087             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7088         }
7089         else
7090         {
7091             /* Despite what the MS docs say, if this is not the last
7092                column, then MS resizes the column to the width of the
7093                largest text string in the column, including headers
7094                and items. This is different from LVSCW_AUTOSIZE in that
7095                LVSCW_AUTOSIZE ignores the header string length. */
7096             cx = 0;
7097
7098             /* retrieve header text */
7099             hdi.mask = HDI_TEXT;
7100             hdi.cchTextMax = DISP_TEXT_SIZE;
7101             hdi.pszText = szDispText;
7102             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7103             {
7104                 HDC hdc = GetDC(infoPtr->hwndSelf);
7105                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7106                 SIZE size;
7107
7108                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7109                     cx = size.cx + TRAILING_HEADER_PADDING;
7110                 /* FIXME: Take into account the header image, if one is present */
7111                 SelectObject(hdc, old_font);
7112                 ReleaseDC(infoPtr->hwndSelf, hdc);
7113             }
7114             cx = max (cx, max_cx);
7115         }
7116     }
7117
7118     if (cx < 0) return FALSE;
7119
7120     /* call header to update the column change */
7121     hdi.mask = HDI_WIDTH;
7122     hdi.cxy = cx;
7123     TRACE("hdi.cxy=%d\n", hdi.cxy);
7124     return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7125 }
7126
7127 /***
7128  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
7129  *
7130  */
7131 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7132 {
7133     HDC hdc_wnd, hdc;
7134     HBITMAP hbm_im, hbm_mask, hbm_orig;
7135     RECT rc;
7136     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7137     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7138     HIMAGELIST himl;
7139
7140     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7141                             ILC_COLOR | ILC_MASK, 2, 2);
7142     hdc_wnd = GetDC(infoPtr->hwndSelf);
7143     hdc = CreateCompatibleDC(hdc_wnd);
7144     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7145     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7146     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7147
7148     rc.left = rc.top = 0;
7149     rc.right = GetSystemMetrics(SM_CXSMICON);
7150     rc.bottom = GetSystemMetrics(SM_CYSMICON);
7151
7152     hbm_orig = SelectObject(hdc, hbm_mask);
7153     FillRect(hdc, &rc, hbr_white);
7154     InflateRect(&rc, -3, -3);
7155     FillRect(hdc, &rc, hbr_black);
7156
7157     SelectObject(hdc, hbm_im);
7158     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7159     SelectObject(hdc, hbm_orig);
7160     ImageList_Add(himl, hbm_im, hbm_mask); 
7161
7162     SelectObject(hdc, hbm_im);
7163     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7164     SelectObject(hdc, hbm_orig);
7165     ImageList_Add(himl, hbm_im, hbm_mask);
7166
7167     DeleteObject(hbm_mask);
7168     DeleteObject(hbm_im);
7169     DeleteDC(hdc);
7170
7171     return himl;
7172 }
7173
7174 /***
7175  * DESCRIPTION:
7176  * Sets the extended listview style.
7177  *
7178  * PARAMETERS:
7179  * [I] infoPtr : valid pointer to the listview structure
7180  * [I] dwMask : mask
7181  * [I] dwStyle : style
7182  *
7183  * RETURN:
7184  *   SUCCESS : previous style
7185  *   FAILURE : 0
7186  */
7187 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7188 {
7189     DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7190
7191     /* set new style */
7192     if (dwMask)
7193         infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7194     else
7195         infoPtr->dwLvExStyle = dwExStyle;
7196
7197     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7198     {
7199         HIMAGELIST himl = 0;
7200         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7201         {
7202             LVITEMW item;
7203             item.mask = LVIF_STATE;
7204             item.stateMask = LVIS_STATEIMAGEMASK;
7205             item.state = INDEXTOSTATEIMAGEMASK(1);
7206             LISTVIEW_SetItemState(infoPtr, -1, &item);
7207
7208             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7209         }
7210         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7211     }
7212     
7213     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7214     {
7215         DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7216         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7217             dwStyle |= HDS_DRAGDROP;
7218         else
7219             dwStyle &= ~HDS_DRAGDROP;
7220         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7221     }
7222
7223     /* GRIDLINES adds decoration at top so changes sizes */
7224     if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7225     {
7226         LISTVIEW_UpdateSize(infoPtr);
7227     }
7228
7229
7230     LISTVIEW_InvalidateList(infoPtr);
7231     return dwOldExStyle;
7232 }
7233
7234 /***
7235  * DESCRIPTION:
7236  * Sets the new hot cursor used during hot tracking and hover selection.
7237  *
7238  * PARAMETER(S):
7239  * [I] infoPtr : valid pointer to the listview structure
7240  * [I] hCursor : the new hot cursor handle
7241  *
7242  * RETURN:
7243  * Returns the previous hot cursor
7244  */
7245 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7246 {
7247     HCURSOR oldCursor = infoPtr->hHotCursor;
7248     
7249     infoPtr->hHotCursor = hCursor;
7250
7251     return oldCursor;
7252 }
7253
7254
7255 /***
7256  * DESCRIPTION:
7257  * Sets the hot item index.
7258  *
7259  * PARAMETERS:
7260  * [I] infoPtr : valid pointer to the listview structure
7261  * [I] iIndex : index
7262  *
7263  * RETURN:
7264  *   SUCCESS : previous hot item index
7265  *   FAILURE : -1 (no hot item)
7266  */
7267 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7268 {
7269     INT iOldIndex = infoPtr->nHotItem;
7270     
7271     infoPtr->nHotItem = iIndex;
7272     
7273     return iOldIndex;
7274 }
7275
7276
7277 /***
7278  * DESCRIPTION:
7279  * Sets the amount of time the cursor must hover over an item before it is selected.
7280  *
7281  * PARAMETER(S):
7282  * [I] infoPtr : valid pointer to the listview structure
7283  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7284  *
7285  * RETURN:
7286  * Returns the previous hover time
7287  */
7288 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7289 {
7290     DWORD oldHoverTime = infoPtr->dwHoverTime;
7291     
7292     infoPtr->dwHoverTime = dwHoverTime;
7293     
7294     return oldHoverTime;
7295 }
7296
7297 /***
7298  * DESCRIPTION:
7299  * Sets spacing for icons of LVS_ICON style.
7300  *
7301  * PARAMETER(S):
7302  * [I] infoPtr : valid pointer to the listview structure
7303  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7304  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7305  *
7306  * RETURN:
7307  *   MAKELONG(oldcx, oldcy)
7308  */
7309 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7310 {
7311     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7312     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7313
7314     TRACE("requested=(%d,%d)\n", cx, cy);
7315     
7316     /* this is supported only for LVS_ICON style */
7317     if (uView != LVS_ICON) return oldspacing;
7318   
7319     /* set to defaults, if instructed to */
7320     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7321     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7322
7323     /* if 0 then compute width
7324      * FIXME: Should scan each item and determine max width of
7325      *        icon or label, then make that the width */
7326     if (cx == 0)
7327         cx = infoPtr->iconSpacing.cx;
7328
7329     /* if 0 then compute height */
7330     if (cy == 0) 
7331         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7332              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7333     
7334
7335     infoPtr->iconSpacing.cx = cx;
7336     infoPtr->iconSpacing.cy = cy;
7337
7338     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7339           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
7340           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7341           infoPtr->ntmHeight);
7342
7343     /* these depend on the iconSpacing */
7344     LISTVIEW_UpdateItemSize(infoPtr);
7345
7346     return oldspacing;
7347 }
7348
7349 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7350 {
7351     INT cx, cy;
7352     
7353     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7354     {
7355         size->cx = cx;
7356         size->cy = cy;
7357     }
7358     else
7359     {
7360         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7361         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7362     }
7363 }
7364
7365 /***
7366  * DESCRIPTION:
7367  * Sets image lists.
7368  *
7369  * PARAMETER(S):
7370  * [I] infoPtr : valid pointer to the listview structure
7371  * [I] nType : image list type
7372  * [I] himl : image list handle
7373  *
7374  * RETURN:
7375  *   SUCCESS : old image list
7376  *   FAILURE : NULL
7377  */
7378 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7379 {
7380     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7381     INT oldHeight = infoPtr->nItemHeight;
7382     HIMAGELIST himlOld = 0;
7383
7384     TRACE("(nType=%d, himl=%p\n", nType, himl);
7385
7386     switch (nType)
7387     {
7388     case LVSIL_NORMAL:
7389         himlOld = infoPtr->himlNormal;
7390         infoPtr->himlNormal = himl;
7391         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7392         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7393     break;
7394
7395     case LVSIL_SMALL:
7396         himlOld = infoPtr->himlSmall;
7397         infoPtr->himlSmall = himl;
7398          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7399     break;
7400
7401     case LVSIL_STATE:
7402         himlOld = infoPtr->himlState;
7403         infoPtr->himlState = himl;
7404         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7405         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7406     break;
7407
7408     default:
7409         ERR("Unknown icon type=%d\n", nType);
7410         return NULL;
7411     }
7412
7413     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7414     if (infoPtr->nItemHeight != oldHeight)
7415         LISTVIEW_UpdateScroll(infoPtr);
7416
7417     return himlOld;
7418 }
7419
7420 /***
7421  * DESCRIPTION:
7422  * Preallocates memory (does *not* set the actual count of items !)
7423  *
7424  * PARAMETER(S):
7425  * [I] infoPtr : valid pointer to the listview structure
7426  * [I] nItems : item count (projected number of items to allocate)
7427  * [I] dwFlags : update flags
7428  *
7429  * RETURN:
7430  *   SUCCESS : TRUE
7431  *   FAILURE : FALSE
7432  */
7433 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7434 {
7435     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7436
7437     if (infoPtr->dwStyle & LVS_OWNERDATA)
7438     {
7439         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7440         INT nOldCount = infoPtr->nItemCount;
7441
7442         if (nItems < nOldCount)
7443         {
7444             RANGE range = { nItems, nOldCount };
7445             ranges_del(infoPtr->selectionRanges, range);
7446             if (infoPtr->nFocusedItem >= nItems)
7447             {
7448                 infoPtr->nFocusedItem = -1;
7449                 SetRectEmpty(&infoPtr->rcFocus);
7450             }
7451         }
7452
7453         infoPtr->nItemCount = nItems;
7454         LISTVIEW_UpdateScroll(infoPtr);
7455
7456         /* the flags are valid only in ownerdata report and list modes */
7457         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7458
7459         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7460             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7461
7462         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7463             LISTVIEW_InvalidateList(infoPtr);
7464         else
7465         {
7466             INT nFrom, nTo;
7467             POINT Origin;
7468             RECT rcErase;
7469             
7470             LISTVIEW_GetOrigin(infoPtr, &Origin);
7471             nFrom = min(nOldCount, nItems);
7472             nTo = max(nOldCount, nItems);
7473     
7474             if (uView == LVS_REPORT)
7475             {
7476                 rcErase.left = 0;
7477                 rcErase.top = nFrom * infoPtr->nItemHeight;
7478                 rcErase.right = infoPtr->nItemWidth;
7479                 rcErase.bottom = nTo * infoPtr->nItemHeight;
7480                 OffsetRect(&rcErase, Origin.x, Origin.y);
7481                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7482                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7483             }
7484             else /* LVS_LIST */
7485             {
7486                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7487
7488                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7489                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7490                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7491                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7492                 OffsetRect(&rcErase, Origin.x, Origin.y);
7493                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7494                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7495
7496                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7497                 rcErase.top = 0;
7498                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7499                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7500                 OffsetRect(&rcErase, Origin.x, Origin.y);
7501                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7502                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7503             }
7504         }
7505     }
7506     else
7507     {
7508         /* According to MSDN for non-LVS_OWNERDATA this is just
7509          * a performance issue. The control allocates its internal
7510          * data structures for the number of items specified. It
7511          * cuts down on the number of memory allocations. Therefore
7512          * we will just issue a WARN here
7513          */
7514         WARN("for non-ownerdata performance option not implemented.\n");
7515     }
7516
7517     return TRUE;
7518 }
7519
7520 /***
7521  * DESCRIPTION:
7522  * Sets the position of an item.
7523  *
7524  * PARAMETER(S):
7525  * [I] infoPtr : valid pointer to the listview structure
7526  * [I] nItem : item index
7527  * [I] pt : coordinate
7528  *
7529  * RETURN:
7530  *   SUCCESS : TRUE
7531  *   FAILURE : FALSE
7532  */
7533 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7534 {
7535     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7536     POINT Origin;
7537
7538     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7539
7540     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7541         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7542
7543     LISTVIEW_GetOrigin(infoPtr, &Origin);
7544
7545     /* This point value seems to be an undocumented feature.
7546      * The best guess is that it means either at the origin, 
7547      * or at true beginning of the list. I will assume the origin. */
7548     if ((pt.x == -1) && (pt.y == -1))
7549         pt = Origin;
7550     
7551     if (uView == LVS_ICON)
7552     {
7553         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7554         pt.y -= ICON_TOP_PADDING;
7555     }
7556     pt.x -= Origin.x;
7557     pt.y -= Origin.y;
7558
7559     infoPtr->bAutoarrange = FALSE;
7560
7561     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7562 }
7563
7564 /***
7565  * DESCRIPTION:
7566  * Sets the state of one or many items.
7567  *
7568  * PARAMETER(S):
7569  * [I] infoPtr : valid pointer to the listview structure
7570  * [I] nItem : item index
7571  * [I] lpLVItem : item or subitem info
7572  *
7573  * RETURN:
7574  *   SUCCESS : TRUE
7575  *   FAILURE : FALSE
7576  */
7577 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7578 {
7579     BOOL bResult = TRUE;
7580     LVITEMW lvItem;
7581
7582     lvItem.iItem = nItem;
7583     lvItem.iSubItem = 0;
7584     lvItem.mask = LVIF_STATE;
7585     lvItem.state = lpLVItem->state;
7586     lvItem.stateMask = lpLVItem->stateMask;
7587     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7588
7589     if (nItem == -1)
7590     {
7591         /* apply to all items */
7592         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7593             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7594     }
7595     else
7596         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7597
7598     /*
7599      * Update selection mark
7600      *
7601      * Investigation on windows 2k showed that selection mark was updated
7602      * whenever a new selection was made, but if the selected item was
7603      * unselected it was not updated.
7604      *
7605      * we are probably still not 100% accurate, but this at least sets the
7606      * proper selection mark when it is needed
7607      */
7608
7609     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7610         (infoPtr->nSelectionMark == -1))
7611     {
7612         int i;
7613         for (i = 0; i < infoPtr->nItemCount; i++)
7614         {
7615             if (infoPtr->uCallbackMask & LVIS_SELECTED)
7616             {
7617                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7618                 {
7619                     infoPtr->nSelectionMark = i;
7620                     break;
7621                 }
7622             }
7623             else if (ranges_contain(infoPtr->selectionRanges, i))
7624             {
7625                 infoPtr->nSelectionMark = i;
7626                 break;
7627             }
7628         }
7629     }
7630
7631     return bResult;
7632 }
7633
7634 /***
7635  * DESCRIPTION:
7636  * Sets the text of an item or subitem.
7637  *
7638  * PARAMETER(S):
7639  * [I] hwnd : window handle
7640  * [I] nItem : item index
7641  * [I] lpLVItem : item or subitem info
7642  * [I] isW : TRUE if input is Unicode
7643  *
7644  * RETURN:
7645  *   SUCCESS : TRUE
7646  *   FAILURE : FALSE
7647  */
7648 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7649 {
7650     LVITEMW lvItem;
7651
7652     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7653     
7654     lvItem.iItem = nItem;
7655     lvItem.iSubItem = lpLVItem->iSubItem;
7656     lvItem.mask = LVIF_TEXT;
7657     lvItem.pszText = lpLVItem->pszText;
7658     lvItem.cchTextMax = lpLVItem->cchTextMax;
7659     
7660     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7661
7662     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
7663 }
7664
7665 /***
7666  * DESCRIPTION:
7667  * Set item index that marks the start of a multiple selection.
7668  *
7669  * PARAMETER(S):
7670  * [I] infoPtr : valid pointer to the listview structure
7671  * [I] nIndex : index
7672  *
7673  * RETURN:
7674  * Index number or -1 if there is no selection mark.
7675  */
7676 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7677 {
7678   INT nOldIndex = infoPtr->nSelectionMark;
7679
7680   TRACE("(nIndex=%d)\n", nIndex);
7681
7682   infoPtr->nSelectionMark = nIndex;
7683
7684   return nOldIndex;
7685 }
7686
7687 /***
7688  * DESCRIPTION:
7689  * Sets the text background color.
7690  *
7691  * PARAMETER(S):
7692  * [I] infoPtr : valid pointer to the listview structure
7693  * [I] clrTextBk : text background color
7694  *
7695  * RETURN:
7696  *   SUCCESS : TRUE
7697  *   FAILURE : FALSE
7698  */
7699 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7700 {
7701     TRACE("(clrTextBk=%x)\n", clrTextBk);
7702
7703     if (infoPtr->clrTextBk != clrTextBk)
7704     {
7705         infoPtr->clrTextBk = clrTextBk;
7706         LISTVIEW_InvalidateList(infoPtr);
7707     }
7708     
7709   return TRUE;
7710 }
7711
7712 /***
7713  * DESCRIPTION:
7714  * Sets the text foreground color.
7715  *
7716  * PARAMETER(S):
7717  * [I] infoPtr : valid pointer to the listview structure
7718  * [I] clrText : text color
7719  *
7720  * RETURN:
7721  *   SUCCESS : TRUE
7722  *   FAILURE : FALSE
7723  */
7724 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7725 {
7726     TRACE("(clrText=%x)\n", clrText);
7727
7728     if (infoPtr->clrText != clrText)
7729     {
7730         infoPtr->clrText = clrText;
7731         LISTVIEW_InvalidateList(infoPtr);
7732     }
7733
7734     return TRUE;
7735 }
7736
7737 /***
7738  * DESCRIPTION:
7739  * Determines which listview item is located at the specified position.
7740  *
7741  * PARAMETER(S):
7742  * [I] infoPtr        : valid pointer to the listview structure
7743  * [I] hwndNewToolTip : handle to new ToolTip
7744  *
7745  * RETURN:
7746  *   old tool tip
7747  */
7748 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7749 {
7750   HWND hwndOldToolTip = infoPtr->hwndToolTip;
7751   infoPtr->hwndToolTip = hwndNewToolTip;
7752   return hwndOldToolTip;
7753 }
7754
7755 /*
7756  * DESCRIPTION:
7757  *   sets the Unicode character format flag for the control
7758  * PARAMETER(S):
7759  *    [I] infoPtr         :valid pointer to the listview structure
7760  *    [I] fUnicode        :true to switch to UNICODE false to switch to ANSI
7761  *
7762  * RETURN:
7763  *    Old Unicode Format
7764  */
7765 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7766 {
7767   BOOL rc = infoPtr->notifyFormat;
7768   infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7769   return rc;
7770 }
7771
7772 /* LISTVIEW_SetWorkAreas */
7773
7774 /***
7775  * DESCRIPTION:
7776  * Callback internally used by LISTVIEW_SortItems()
7777  *
7778  * PARAMETER(S):
7779  * [I] first : pointer to first ITEM_INFO to compare
7780  * [I] second : pointer to second ITEM_INFO to compare
7781  * [I] lParam : HWND of control
7782  *
7783  * RETURN:
7784  *   if first comes before second : negative
7785  *   if first comes after second : positive
7786  *   if first and second are equivalent : zero
7787  */
7788 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7789 {
7790   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7791   ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7792   ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7793
7794   /* Forward the call to the client defined callback */
7795   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7796 }
7797
7798 /***
7799  * DESCRIPTION:
7800  * Sorts the listview items.
7801  *
7802  * PARAMETER(S):
7803  * [I] infoPtr : valid pointer to the listview structure
7804  * [I] pfnCompare : application-defined value
7805  * [I] lParamSort : pointer to comparison callback
7806  *
7807  * RETURN:
7808  *   SUCCESS : TRUE
7809  *   FAILURE : FALSE
7810  */
7811 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7812 {
7813     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7814     HDPA hdpaSubItems;
7815     ITEM_INFO *lpItem;
7816     LPVOID selectionMarkItem;
7817     LVITEMW item;
7818     int i;
7819
7820     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7821
7822     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7823
7824     if (!pfnCompare) return FALSE;
7825     if (!infoPtr->hdpaItems) return FALSE;
7826
7827     /* if there are 0 or 1 items, there is no need to sort */
7828     if (infoPtr->nItemCount < 2) return TRUE;
7829
7830     if (infoPtr->nFocusedItem >= 0)
7831     {
7832         hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7833         lpItem = DPA_GetPtr(hdpaSubItems, 0);
7834         if (lpItem) lpItem->state |= LVIS_FOCUSED;
7835     }
7836     /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7837     /*        clear the lpItem->state for non-selected ones */
7838     /*        remove the selection ranges */
7839     
7840     infoPtr->pfnCompare = pfnCompare;
7841     infoPtr->lParamSort = lParamSort;
7842     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7843
7844     /* Adjust selections and indices so that they are the way they should
7845      * be after the sort (otherwise, the list items move around, but
7846      * whatever is at the item's previous original position will be
7847      * selected instead)
7848      */
7849     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7850     for (i=0; i < infoPtr->nItemCount; i++)
7851     {
7852         hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7853         lpItem = DPA_GetPtr(hdpaSubItems, 0);
7854
7855         if (lpItem->state & LVIS_SELECTED)
7856         {
7857             item.state = LVIS_SELECTED;
7858             item.stateMask = LVIS_SELECTED;
7859             LISTVIEW_SetItemState(infoPtr, i, &item);
7860         }
7861         if (lpItem->state & LVIS_FOCUSED)
7862         {
7863             infoPtr->nFocusedItem = i;
7864             lpItem->state &= ~LVIS_FOCUSED;
7865         }
7866     }
7867     if (selectionMarkItem != NULL)
7868         infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7869     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7870
7871     /* refresh the display */
7872     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7873         LISTVIEW_InvalidateList(infoPtr);
7874
7875     return TRUE;
7876 }
7877
7878 /***
7879  * DESCRIPTION:
7880  * Update theme handle after a theme change.
7881  *
7882  * PARAMETER(S):
7883  * [I] infoPtr : valid pointer to the listview structure
7884  *
7885  * RETURN:
7886  *   SUCCESS : 0
7887  *   FAILURE : something else
7888  */
7889 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7890 {
7891     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7892     CloseThemeData(theme);
7893     OpenThemeData(infoPtr->hwndSelf, themeClass);
7894     return 0;
7895 }
7896
7897 /***
7898  * DESCRIPTION:
7899  * Updates an items or rearranges the listview control.
7900  *
7901  * PARAMETER(S):
7902  * [I] infoPtr : valid pointer to the listview structure
7903  * [I] nItem : item index
7904  *
7905  * RETURN:
7906  *   SUCCESS : TRUE
7907  *   FAILURE : FALSE
7908  */
7909 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7910 {
7911     TRACE("(nItem=%d)\n", nItem);
7912
7913     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7914
7915     /* rearrange with default alignment style */
7916     if (is_autoarrange(infoPtr))
7917         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7918     else
7919         LISTVIEW_InvalidateItem(infoPtr, nItem);
7920
7921     return TRUE;
7922 }
7923
7924 /***
7925  * DESCRIPTION:
7926  * Draw the track line at the place defined in the infoPtr structure.
7927  * The line is drawn with a XOR pen so drawing the line for the second time
7928  * in the same place erases the line.
7929  *
7930  * PARAMETER(S):
7931  * [I] infoPtr : valid pointer to the listview structure
7932  *
7933  * RETURN:
7934  *   SUCCESS : TRUE
7935  *   FAILURE : FALSE
7936  */
7937 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7938 {
7939     HPEN hOldPen;
7940     HDC hdc;
7941     INT oldROP;
7942
7943     if (infoPtr->xTrackLine == -1)
7944         return FALSE;
7945
7946     if (!(hdc = GetDC(infoPtr->hwndSelf)))
7947         return FALSE;
7948     hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7949     oldROP = SetROP2(hdc, R2_XORPEN);
7950     MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7951     LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7952     SetROP2(hdc, oldROP);
7953     SelectObject(hdc, hOldPen);
7954     ReleaseDC(infoPtr->hwndSelf, hdc);
7955     return TRUE;
7956 }
7957
7958 /***
7959  * DESCRIPTION:
7960  * Called when an edit control should be displayed. This function is called after
7961  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7962  *
7963  * PARAMETER(S):
7964  * [I] hwnd : Handle to the listview
7965  * [I] uMsg : WM_TIMER (ignored)
7966  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7967  * [I] dwTimer : The elapsed time (ignored)
7968  *
7969  * RETURN:
7970  *   None.
7971  */
7972 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7973 {
7974     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7975     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7976
7977     KillTimer(hwnd, idEvent);
7978     editItem->fEnabled = FALSE;
7979     /* check if the item is still selected */
7980     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7981         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7982 }
7983
7984 /***
7985  * DESCRIPTION:
7986  * Creates the listview control - the WM_NCCREATE phase.
7987  *
7988  * PARAMETER(S):
7989  * [I] hwnd : window handle
7990  * [I] lpcs : the create parameters
7991  *
7992  * RETURN:
7993  *   Success: TRUE
7994  *   Failure: FALSE
7995  */
7996 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7997 {
7998   LISTVIEW_INFO *infoPtr;
7999   LOGFONTW logFont;
8000
8001   TRACE("(lpcs=%p)\n", lpcs);
8002
8003   /* initialize info pointer */
8004   infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8005   if (!infoPtr) return FALSE;
8006
8007   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8008
8009   infoPtr->hwndSelf = hwnd;
8010   infoPtr->dwStyle = lpcs->style;    /* Note: may be changed in WM_CREATE */
8011   /* determine the type of structures to use */
8012   infoPtr->hwndNotify = lpcs->hwndParent;
8013   /* infoPtr->notifyFormat will be filled in WM_CREATE */
8014
8015   /* initialize color information  */
8016   infoPtr->clrBk = CLR_NONE;
8017   infoPtr->clrText = CLR_DEFAULT;
8018   infoPtr->clrTextBk = CLR_DEFAULT;
8019   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8020
8021   /* set default values */
8022   infoPtr->nFocusedItem = -1;
8023   infoPtr->nSelectionMark = -1;
8024   infoPtr->nHotItem = -1;
8025   infoPtr->bRedraw = TRUE;
8026   infoPtr->bNoItemMetrics = TRUE;
8027   infoPtr->bDoChangeNotify = TRUE;
8028   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8029   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8030   infoPtr->nEditLabelItem = -1;
8031   infoPtr->dwHoverTime = -1; /* default system hover time */
8032   infoPtr->nMeasureItemHeight = 0;
8033   infoPtr->xTrackLine = -1;  /* no track line */
8034   infoPtr->itemEdit.fEnabled = FALSE;
8035
8036   /* get default font (icon title) */
8037   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8038   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8039   infoPtr->hFont = infoPtr->hDefaultFont;
8040   LISTVIEW_SaveTextMetrics(infoPtr);
8041
8042   /* allocate memory for the data structure */
8043   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8044   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8045   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
8046   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
8047   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8048   return TRUE;
8049
8050 fail:
8051     DestroyWindow(infoPtr->hwndHeader);
8052     ranges_destroy(infoPtr->selectionRanges);
8053     DPA_Destroy(infoPtr->hdpaItems);
8054     DPA_Destroy(infoPtr->hdpaPosX);
8055     DPA_Destroy(infoPtr->hdpaPosY);
8056     DPA_Destroy(infoPtr->hdpaColumns);
8057     Free(infoPtr);
8058     return FALSE;
8059 }
8060
8061 /***
8062  * DESCRIPTION:
8063  * Creates the listview control - the WM_CREATE phase. Most of the data is
8064  * already set up in LISTVIEW_NCCreate
8065  *
8066  * PARAMETER(S):
8067  * [I] hwnd : window handle
8068  * [I] lpcs : the create parameters
8069  *
8070  * RETURN:
8071  *   Success: 0
8072  *   Failure: -1
8073  */
8074 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8075 {
8076   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8077   UINT uView = lpcs->style & LVS_TYPEMASK;
8078   DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
8079
8080   TRACE("(lpcs=%p)\n", lpcs);
8081
8082   infoPtr->dwStyle = lpcs->style;
8083   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8084                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8085
8086   /* setup creation flags */
8087   dFlags |= (LVS_NOSORTHEADER & lpcs->style) ? 0 : HDS_BUTTONS;
8088   dFlags |= (LVS_NOCOLUMNHEADER & lpcs->style) ? HDS_HIDDEN : 0;
8089
8090   /* create header */
8091   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
8092     0, 0, 0, 0, hwnd, NULL,
8093     lpcs->hInstance, NULL);
8094   if (!infoPtr->hwndHeader) return -1;
8095
8096   /* set header unicode format */
8097   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
8098
8099   /* set header font */
8100   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
8101
8102   /* init item size to avoid division by 0 */
8103   LISTVIEW_UpdateItemSize (infoPtr);
8104
8105   if (uView == LVS_REPORT)
8106   {
8107     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8108     {
8109       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8110     }
8111     LISTVIEW_UpdateSize(infoPtr);
8112     LISTVIEW_UpdateScroll(infoPtr);
8113   }
8114
8115   OpenThemeData(hwnd, themeClass);
8116
8117   /* initialize the icon sizes */
8118   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8119   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8120   return 0;
8121 }
8122
8123 /***
8124  * DESCRIPTION:
8125  * Destroys the listview control.
8126  *
8127  * PARAMETER(S):
8128  * [I] infoPtr : valid pointer to the listview structure
8129  *
8130  * RETURN:
8131  *   Success: 0
8132  *   Failure: -1
8133  */
8134 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8135 {
8136     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8137     CloseThemeData(theme);
8138     return 0;
8139 }
8140
8141 /***
8142  * DESCRIPTION:
8143  * Enables the listview control.
8144  *
8145  * PARAMETER(S):
8146  * [I] infoPtr : valid pointer to the listview structure
8147  * [I] bEnable : specifies whether to enable or disable the window
8148  *
8149  * RETURN:
8150  *   SUCCESS : TRUE
8151  *   FAILURE : FALSE
8152  */
8153 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8154 {
8155     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8156         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8157     return TRUE;
8158 }
8159
8160 /***
8161  * DESCRIPTION:
8162  * Erases the background of the listview control.
8163  *
8164  * PARAMETER(S):
8165  * [I] infoPtr : valid pointer to the listview structure
8166  * [I] hdc : device context handle
8167  *
8168  * RETURN:
8169  *   SUCCESS : TRUE
8170  *   FAILURE : FALSE
8171  */
8172 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8173 {
8174     RECT rc;
8175
8176     TRACE("(hdc=%p)\n", hdc);
8177
8178     if (!GetClipBox(hdc, &rc)) return FALSE;
8179
8180     /* for double buffered controls we need to do this during refresh */
8181     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8182
8183     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8184 }
8185         
8186
8187 /***
8188  * DESCRIPTION:
8189  * Helper function for LISTVIEW_[HV]Scroll *only*.
8190  * Performs vertical/horizontal scrolling by a give amount.
8191  *
8192  * PARAMETER(S):
8193  * [I] infoPtr : valid pointer to the listview structure
8194  * [I] dx : amount of horizontal scroll
8195  * [I] dy : amount of vertical scroll
8196  */
8197 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8198 {
8199     /* now we can scroll the list */
8200     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
8201                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8202     /* if we have focus, adjust rect */
8203     OffsetRect(&infoPtr->rcFocus, dx, dy);
8204     UpdateWindow(infoPtr->hwndSelf);
8205 }
8206
8207 /***
8208  * DESCRIPTION:
8209  * Performs vertical scrolling.
8210  *
8211  * PARAMETER(S):
8212  * [I] infoPtr : valid pointer to the listview structure
8213  * [I] nScrollCode : scroll code
8214  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8215  * [I] hScrollWnd  : scrollbar control window handle
8216  *
8217  * RETURN:
8218  * Zero
8219  *
8220  * NOTES:
8221  *   SB_LINEUP/SB_LINEDOWN:
8222  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
8223  *        for LVS_REPORT is 1 line
8224  *        for LVS_LIST cannot occur
8225  *
8226  */
8227 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
8228                                 INT nScrollDiff, HWND hScrollWnd)
8229 {
8230     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8231     INT nOldScrollPos, nNewScrollPos;
8232     SCROLLINFO scrollInfo;
8233     BOOL is_an_icon;
8234
8235     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8236         debugscrollcode(nScrollCode), nScrollDiff);
8237
8238     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8239
8240     scrollInfo.cbSize = sizeof(SCROLLINFO);
8241     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8242
8243     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8244
8245     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8246
8247     nOldScrollPos = scrollInfo.nPos;
8248     switch (nScrollCode)
8249     {
8250     case SB_INTERNAL:
8251         break;
8252
8253     case SB_LINEUP:
8254         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8255         break;
8256
8257     case SB_LINEDOWN:
8258         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8259         break;
8260
8261     case SB_PAGEUP:
8262         nScrollDiff = -scrollInfo.nPage;
8263         break;
8264
8265     case SB_PAGEDOWN:
8266         nScrollDiff = scrollInfo.nPage;
8267         break;
8268
8269     case SB_THUMBPOSITION:
8270     case SB_THUMBTRACK:
8271         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8272         break;
8273
8274     default:
8275         nScrollDiff = 0;
8276     }
8277
8278     /* quit right away if pos isn't changing */
8279     if (nScrollDiff == 0) return 0;
8280     
8281     /* calculate new position, and handle overflows */
8282     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8283     if (nScrollDiff > 0) {
8284         if (nNewScrollPos < nOldScrollPos ||
8285             nNewScrollPos > scrollInfo.nMax)
8286             nNewScrollPos = scrollInfo.nMax;
8287     } else {
8288         if (nNewScrollPos > nOldScrollPos ||
8289             nNewScrollPos < scrollInfo.nMin)
8290             nNewScrollPos = scrollInfo.nMin;
8291     }
8292
8293     /* set the new position, and reread in case it changed */
8294     scrollInfo.fMask = SIF_POS;
8295     scrollInfo.nPos = nNewScrollPos;
8296     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8297     
8298     /* carry on only if it really changed */
8299     if (nNewScrollPos == nOldScrollPos) return 0;
8300     
8301     /* now adjust to client coordinates */
8302     nScrollDiff = nOldScrollPos - nNewScrollPos;
8303     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8304    
8305     /* and scroll the window */ 
8306     scroll_list(infoPtr, 0, nScrollDiff);
8307
8308     return 0;
8309 }
8310
8311 /***
8312  * DESCRIPTION:
8313  * Performs horizontal scrolling.
8314  *
8315  * PARAMETER(S):
8316  * [I] infoPtr : valid pointer to the listview structure
8317  * [I] nScrollCode : scroll code
8318  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8319  * [I] hScrollWnd  : scrollbar control window handle
8320  *
8321  * RETURN:
8322  * Zero
8323  *
8324  * NOTES:
8325  *   SB_LINELEFT/SB_LINERIGHT:
8326  *        for LVS_ICON, LVS_SMALLICON  1 pixel
8327  *        for LVS_REPORT is 1 pixel
8328  *        for LVS_LIST  is 1 column --> which is a 1 because the
8329  *                                      scroll is based on columns not pixels
8330  *
8331  */
8332 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8333                                 INT nScrollDiff, HWND hScrollWnd)
8334 {
8335     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8336     INT nOldScrollPos, nNewScrollPos;
8337     SCROLLINFO scrollInfo;
8338
8339     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
8340         debugscrollcode(nScrollCode), nScrollDiff);
8341
8342     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8343
8344     scrollInfo.cbSize = sizeof(SCROLLINFO);
8345     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8346
8347     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8348
8349     nOldScrollPos = scrollInfo.nPos;
8350
8351     switch (nScrollCode)
8352     {
8353     case SB_INTERNAL:
8354         break;
8355
8356     case SB_LINELEFT:
8357         nScrollDiff = -1;
8358         break;
8359
8360     case SB_LINERIGHT:
8361         nScrollDiff = 1;
8362         break;
8363
8364     case SB_PAGELEFT:
8365         nScrollDiff = -scrollInfo.nPage;
8366         break;
8367
8368     case SB_PAGERIGHT:
8369         nScrollDiff = scrollInfo.nPage;
8370         break;
8371
8372     case SB_THUMBPOSITION:
8373     case SB_THUMBTRACK:
8374         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8375         break;
8376
8377     default:
8378         nScrollDiff = 0;
8379     }
8380
8381     /* quit right away if pos isn't changing */
8382     if (nScrollDiff == 0) return 0;
8383     
8384     /* calculate new position, and handle overflows */
8385     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8386     if (nScrollDiff > 0) {
8387         if (nNewScrollPos < nOldScrollPos ||
8388             nNewScrollPos > scrollInfo.nMax)
8389             nNewScrollPos = scrollInfo.nMax;
8390     } else {
8391         if (nNewScrollPos > nOldScrollPos ||
8392             nNewScrollPos < scrollInfo.nMin)
8393             nNewScrollPos = scrollInfo.nMin;
8394     }
8395
8396     /* set the new position, and reread in case it changed */
8397     scrollInfo.fMask = SIF_POS;
8398     scrollInfo.nPos = nNewScrollPos;
8399     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8400     
8401     /* carry on only if it really changed */
8402     if (nNewScrollPos == nOldScrollPos) return 0;
8403     
8404     if(uView == LVS_REPORT)
8405         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8406       
8407     /* now adjust to client coordinates */
8408     nScrollDiff = nOldScrollPos - nNewScrollPos;
8409     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8410    
8411     /* and scroll the window */
8412     scroll_list(infoPtr, nScrollDiff, 0);
8413
8414   return 0;
8415 }
8416
8417 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8418 {
8419     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8420     INT gcWheelDelta = 0;
8421     INT pulScrollLines = 3;
8422     SCROLLINFO scrollInfo;
8423
8424     TRACE("(wheelDelta=%d)\n", wheelDelta);
8425
8426     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8427     gcWheelDelta -= wheelDelta;
8428
8429     scrollInfo.cbSize = sizeof(SCROLLINFO);
8430     scrollInfo.fMask = SIF_POS;
8431
8432     switch(uView)
8433     {
8434     case LVS_ICON:
8435     case LVS_SMALLICON:
8436        /*
8437         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8438         *  should be fixed in the future.
8439         */
8440         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8441                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8442         break;
8443
8444     case LVS_REPORT:
8445         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8446         {
8447             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8448             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8449             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8450         }
8451         break;
8452
8453     case LVS_LIST:
8454         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8455         break;
8456     }
8457     return 0;
8458 }
8459
8460 /***
8461  * DESCRIPTION:
8462  * ???
8463  *
8464  * PARAMETER(S):
8465  * [I] infoPtr : valid pointer to the listview structure
8466  * [I] nVirtualKey : virtual key
8467  * [I] lKeyData : key data
8468  *
8469  * RETURN:
8470  * Zero
8471  */
8472 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8473 {
8474   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8475   HWND hwndSelf = infoPtr->hwndSelf;
8476   INT nItem = -1;
8477   NMLVKEYDOWN nmKeyDown;
8478
8479   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8480
8481   /* send LVN_KEYDOWN notification */
8482   nmKeyDown.wVKey = nVirtualKey;
8483   nmKeyDown.flags = 0;
8484   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8485   if (!IsWindow(hwndSelf))
8486     return 0;
8487
8488   switch (nVirtualKey)
8489   {
8490   case VK_SPACE:
8491     nItem = infoPtr->nFocusedItem;
8492     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8493         toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8494     break;
8495
8496   case VK_RETURN:
8497     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8498     {
8499         if (!notify(infoPtr, NM_RETURN)) return 0;
8500         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8501     }
8502     break;
8503
8504   case VK_HOME:
8505     if (infoPtr->nItemCount > 0)
8506       nItem = 0;
8507     break;
8508
8509   case VK_END:
8510     if (infoPtr->nItemCount > 0)
8511       nItem = infoPtr->nItemCount - 1;
8512     break;
8513
8514   case VK_LEFT:
8515     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8516     break;
8517
8518   case VK_UP:
8519     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8520     break;
8521
8522   case VK_RIGHT:
8523     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8524     break;
8525
8526   case VK_DOWN:
8527     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8528     break;
8529
8530   case VK_PRIOR:
8531     if (uView == LVS_REPORT)
8532     {
8533       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8534       if (infoPtr->nFocusedItem == topidx)
8535         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8536       else
8537         nItem = topidx;
8538     }
8539     else
8540       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8541                                     * LISTVIEW_GetCountPerRow(infoPtr);
8542     if(nItem < 0) nItem = 0;
8543     break;
8544
8545   case VK_NEXT:
8546     if (uView == LVS_REPORT)
8547     {
8548       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8549       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8550       if (infoPtr->nFocusedItem == topidx + cnt - 1)
8551         nItem = infoPtr->nFocusedItem + cnt - 1;
8552       else
8553         nItem = topidx + cnt - 1;
8554     }
8555     else
8556       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8557                                     * LISTVIEW_GetCountPerRow(infoPtr);
8558     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8559     break;
8560   }
8561
8562   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8563       LISTVIEW_KeySelection(infoPtr, nItem);
8564
8565   return 0;
8566 }
8567
8568 /***
8569  * DESCRIPTION:
8570  * Kills the focus.
8571  *
8572  * PARAMETER(S):
8573  * [I] infoPtr : valid pointer to the listview structure
8574  *
8575  * RETURN:
8576  * Zero
8577  */
8578 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8579 {
8580     TRACE("()\n");
8581
8582     /* if we did not have the focus, there's nothing to do */
8583     if (!infoPtr->bFocus) return 0;
8584    
8585     /* send NM_KILLFOCUS notification */
8586     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8587
8588     /* if we have a focus rectagle, get rid of it */
8589     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8590     
8591     /* set window focus flag */
8592     infoPtr->bFocus = FALSE;
8593
8594     /* invalidate the selected items before resetting focus flag */
8595     LISTVIEW_InvalidateSelectedItems(infoPtr);
8596     
8597     return 0;
8598 }
8599
8600 /***
8601  * DESCRIPTION:
8602  * Processes double click messages (left mouse button).
8603  *
8604  * PARAMETER(S):
8605  * [I] infoPtr : valid pointer to the listview structure
8606  * [I] wKey : key flag
8607  * [I] x,y : mouse coordinate
8608  *
8609  * RETURN:
8610  * Zero
8611  */
8612 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8613 {
8614     LVHITTESTINFO htInfo;
8615
8616     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8617     
8618     /* Cancel the item edition if any */
8619     if (infoPtr->itemEdit.fEnabled)
8620     {
8621       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8622       infoPtr->itemEdit.fEnabled = FALSE;
8623     }
8624
8625     /* send NM_RELEASEDCAPTURE notification */
8626     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8627
8628     htInfo.pt.x = x;
8629     htInfo.pt.y = y;
8630
8631     /* send NM_DBLCLK notification */
8632     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8633     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8634
8635     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8636     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8637
8638     return 0;
8639 }
8640
8641 /***
8642  * DESCRIPTION:
8643  * Processes mouse down messages (left mouse button).
8644  *
8645  * PARAMETERS:
8646  *   infoPtr  [I ] valid pointer to the listview structure
8647  *   wKey     [I ] key flag
8648  *   x,y      [I ] mouse coordinate
8649  *
8650  * RETURN:
8651  *   Zero
8652  */
8653 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8654 {
8655   LVHITTESTINFO lvHitTestInfo;
8656   static BOOL bGroupSelect = TRUE;
8657   POINT pt = { x, y };
8658   INT nItem;
8659
8660   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8661
8662   /* send NM_RELEASEDCAPTURE notification */
8663   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8664
8665   /* set left button down flag and record the click position */
8666   infoPtr->bLButtonDown = TRUE;
8667   infoPtr->ptClickPos = pt;
8668   infoPtr->bDragging = FALSE;
8669
8670   lvHitTestInfo.pt.x = x;
8671   lvHitTestInfo.pt.y = y;
8672
8673   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8674   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8675   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8676   {
8677     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8678     {
8679         toggle_checkbox_state(infoPtr, nItem);
8680         return 0;
8681     }
8682
8683     if (infoPtr->dwStyle & LVS_SINGLESEL)
8684     {
8685       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8686         infoPtr->nEditLabelItem = nItem;
8687       else
8688         LISTVIEW_SetSelection(infoPtr, nItem);
8689     }
8690     else
8691     {
8692       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8693       {
8694         if (bGroupSelect)
8695         {
8696           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8697           LISTVIEW_SetItemFocus(infoPtr, nItem);
8698           infoPtr->nSelectionMark = nItem;
8699         }
8700         else
8701         {
8702           LVITEMW item;
8703
8704           item.state = LVIS_SELECTED | LVIS_FOCUSED;
8705           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8706
8707           LISTVIEW_SetItemState(infoPtr,nItem,&item);
8708           infoPtr->nSelectionMark = nItem;
8709         }
8710       }
8711       else if (wKey & MK_CONTROL)
8712       {
8713         LVITEMW item;
8714
8715         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8716         
8717         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8718         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8719         LISTVIEW_SetItemState(infoPtr, nItem, &item);
8720         infoPtr->nSelectionMark = nItem;
8721       }
8722       else  if (wKey & MK_SHIFT)
8723       {
8724         LISTVIEW_SetGroupSelection(infoPtr, nItem);
8725       }
8726       else
8727       {
8728         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8729           infoPtr->nEditLabelItem = nItem;
8730
8731         /* set selection (clears other pre-existing selections) */
8732         LISTVIEW_SetSelection(infoPtr, nItem);
8733       }
8734     }
8735
8736     if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8737         if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8738   }
8739   else
8740   {
8741     /* remove all selections */
8742     LISTVIEW_DeselectAll(infoPtr);
8743     ReleaseCapture();
8744   }
8745   
8746   return 0;
8747 }
8748
8749 /***
8750  * DESCRIPTION:
8751  * Processes mouse up messages (left mouse button).
8752  *
8753  * PARAMETERS:
8754  *   infoPtr [I ] valid pointer to the listview structure
8755  *   wKey    [I ] key flag
8756  *   x,y     [I ] mouse coordinate
8757  *
8758  * RETURN:
8759  *   Zero
8760  */
8761 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8762 {
8763     LVHITTESTINFO lvHitTestInfo;
8764     
8765     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8766
8767     if (!infoPtr->bLButtonDown) return 0;
8768
8769     lvHitTestInfo.pt.x = x;
8770     lvHitTestInfo.pt.y = y;
8771
8772     /* send NM_CLICK notification */
8773     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8774     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8775
8776     /* set left button flag */
8777     infoPtr->bLButtonDown = FALSE;
8778
8779     if (infoPtr->bDragging)
8780     {
8781         infoPtr->bDragging = FALSE;
8782         return 0;
8783     }
8784
8785     /* if we clicked on a selected item, edit the label */
8786     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8787     {
8788         /* we want to make sure the user doesn't want to do a double click. So we will
8789          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8790          */
8791         infoPtr->itemEdit.fEnabled = TRUE;
8792         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8793         SetTimer(infoPtr->hwndSelf,
8794             (UINT_PTR)&infoPtr->itemEdit,
8795             GetDoubleClickTime(),
8796             LISTVIEW_DelayedEditItem);
8797     }
8798
8799     if (!infoPtr->bFocus)
8800         SetFocus(infoPtr->hwndSelf);
8801
8802     return 0;
8803 }
8804
8805 /***
8806  * DESCRIPTION:
8807  * Destroys the listview control (called after WM_DESTROY).
8808  *
8809  * PARAMETER(S):
8810  * [I] infoPtr : valid pointer to the listview structure
8811  *
8812  * RETURN:
8813  * Zero
8814  */
8815 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8816 {
8817   TRACE("()\n");
8818
8819   /* delete all items */
8820   LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8821
8822   /* destroy data structure */
8823   DPA_Destroy(infoPtr->hdpaItems);
8824   DPA_Destroy(infoPtr->hdpaPosX);
8825   DPA_Destroy(infoPtr->hdpaPosY);
8826   DPA_Destroy(infoPtr->hdpaColumns);
8827   ranges_destroy(infoPtr->selectionRanges);
8828
8829   /* destroy image lists */
8830   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8831   {
8832       if (infoPtr->himlNormal)
8833           ImageList_Destroy(infoPtr->himlNormal);
8834       if (infoPtr->himlSmall)
8835           ImageList_Destroy(infoPtr->himlSmall);
8836       if (infoPtr->himlState)
8837           ImageList_Destroy(infoPtr->himlState);
8838   }
8839
8840   /* destroy font, bkgnd brush */
8841   infoPtr->hFont = 0;
8842   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8843   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8844
8845   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8846
8847   /* free listview info pointer*/
8848   Free(infoPtr);
8849
8850   return 0;
8851 }
8852
8853 /***
8854  * DESCRIPTION:
8855  * Handles notifications from header.
8856  *
8857  * PARAMETER(S):
8858  * [I] infoPtr : valid pointer to the listview structure
8859  * [I] nCtrlId : control identifier
8860  * [I] lpnmh : notification information
8861  *
8862  * RETURN:
8863  * Zero
8864  */
8865 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8866 {
8867     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8868     HWND hwndSelf = infoPtr->hwndSelf;
8869     
8870     TRACE("(lpnmh=%p)\n", lpnmh);
8871
8872     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8873     
8874     switch (lpnmh->hdr.code)
8875     {    
8876         case HDN_TRACKW:
8877         case HDN_TRACKA:
8878         {
8879             COLUMN_INFO *lpColumnInfo;
8880             POINT ptOrigin;
8881             INT x;
8882             
8883             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8884                 break;
8885
8886             /* remove the old line (if any) */
8887             LISTVIEW_DrawTrackLine(infoPtr);
8888             
8889             /* compute & draw the new line */
8890             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8891             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8892             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8893             infoPtr->xTrackLine = x + ptOrigin.x;
8894             LISTVIEW_DrawTrackLine(infoPtr);
8895             break;
8896         }
8897         
8898         case HDN_ENDTRACKA:
8899         case HDN_ENDTRACKW:
8900             /* remove the track line (if any) */
8901             LISTVIEW_DrawTrackLine(infoPtr);
8902             infoPtr->xTrackLine = -1;
8903             break;
8904             
8905         case HDN_ENDDRAG:
8906             FIXME("Changing column order not implemented\n");
8907             return TRUE;
8908             
8909         case HDN_ITEMCHANGINGW:
8910         case HDN_ITEMCHANGINGA:
8911             return notify_forward_header(infoPtr, lpnmh);
8912             
8913         case HDN_ITEMCHANGEDW:
8914         case HDN_ITEMCHANGEDA:
8915         {
8916             COLUMN_INFO *lpColumnInfo;
8917             INT dx, cxy;
8918             
8919             notify_forward_header(infoPtr, lpnmh);
8920             if (!IsWindow(hwndSelf))
8921                 break;
8922
8923             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8924             {
8925                 HDITEMW hdi;
8926     
8927                 hdi.mask = HDI_WIDTH;
8928                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8929                 cxy = hdi.cxy;
8930             }
8931             else
8932                 cxy = lpnmh->pitem->cxy;
8933             
8934             /* determine how much we change since the last know position */
8935             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8936             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8937             if (dx != 0)
8938             {
8939                 lpColumnInfo->rcHeader.right += dx;
8940                 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8941                     LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8942                 else
8943                 {
8944                     /* only needs to update the scrolls */
8945                     infoPtr->nItemWidth += dx;
8946                     LISTVIEW_UpdateScroll(infoPtr);
8947                 }
8948                 LISTVIEW_UpdateItemSize(infoPtr);
8949                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8950                 {
8951                     POINT ptOrigin;
8952                     RECT rcCol = lpColumnInfo->rcHeader;
8953                     
8954                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8955                     OffsetRect(&rcCol, ptOrigin.x, 0);
8956                     
8957                     rcCol.top = infoPtr->rcList.top;
8958                     rcCol.bottom = infoPtr->rcList.bottom;
8959
8960                     /* resizing left-aligned columns leaves most of the left side untouched */
8961                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8962                     {
8963                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8964                         if (dx > 0)
8965                             nMaxDirty += dx;
8966                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8967                     }
8968
8969                     /* when shrinking the last column clear the now unused field */
8970                     if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8971                         rcCol.right -= dx;
8972
8973                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8974                 }
8975             }
8976         }
8977         break;
8978
8979         case HDN_ITEMCLICKW:
8980         case HDN_ITEMCLICKA:
8981         {
8982             /* Handle sorting by Header Column */
8983             NMLISTVIEW nmlv;
8984
8985             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8986             nmlv.iItem = -1;
8987             nmlv.iSubItem = lpnmh->iItem;
8988             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8989             notify_forward_header(infoPtr, lpnmh);
8990         }
8991         break;
8992
8993         case HDN_DIVIDERDBLCLICKW:
8994         case HDN_DIVIDERDBLCLICKA:
8995             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8996             break;
8997     }
8998
8999     return 0;
9000 }
9001
9002 /***
9003  * DESCRIPTION:
9004  * Paint non-client area of control.
9005  *
9006  * PARAMETER(S):
9007  * [I] infoPtr : valid pointer to the listview structureof the sender
9008  * [I] region : update region
9009  *
9010  * RETURN:
9011  *  TRUE  - frame was painted
9012  *  FALSE - call default window proc
9013  */
9014 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9015 {
9016     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9017     HDC dc;
9018     RECT r;
9019     HRGN cliprgn;
9020     int cxEdge = GetSystemMetrics (SM_CXEDGE),
9021         cyEdge = GetSystemMetrics (SM_CYEDGE);
9022
9023     if (!theme) return FALSE;
9024
9025     GetWindowRect(infoPtr->hwndSelf, &r);
9026
9027     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9028         r.right - cxEdge, r.bottom - cyEdge);
9029     if (region != (HRGN)1)
9030         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9031     OffsetRect(&r, -r.left, -r.top);
9032
9033     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9034     OffsetRect(&r, -r.left, -r.top);
9035
9036     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9037         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9038     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9039     ReleaseDC(infoPtr->hwndSelf, dc);
9040
9041     /* Call default proc to get the scrollbars etc. painted */
9042     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9043
9044     return TRUE;
9045 }
9046
9047 /***
9048  * DESCRIPTION:
9049  * Determines the type of structure to use.
9050  *
9051  * PARAMETER(S):
9052  * [I] infoPtr : valid pointer to the listview structureof the sender
9053  * [I] hwndFrom : listview window handle
9054  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9055  *
9056  * RETURN:
9057  * Zero
9058  */
9059 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9060 {
9061     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9062
9063     if (nCommand == NF_REQUERY)
9064         infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9065
9066     return infoPtr->notifyFormat;
9067 }
9068
9069 /***
9070  * DESCRIPTION:
9071  * Paints/Repaints the listview control.
9072  *
9073  * PARAMETER(S):
9074  * [I] infoPtr : valid pointer to the listview structure
9075  * [I] hdc : device context handle
9076  *
9077  * RETURN:
9078  * Zero
9079  */
9080 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9081 {
9082     TRACE("(hdc=%p)\n", hdc);
9083
9084     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9085     {
9086         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
9087         
9088         infoPtr->bNoItemMetrics = FALSE;
9089         LISTVIEW_UpdateItemSize(infoPtr);
9090         if (uView == LVS_ICON || uView == LVS_SMALLICON)
9091             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9092         LISTVIEW_UpdateScroll(infoPtr);
9093     }
9094
9095     UpdateWindow(infoPtr->hwndHeader);
9096
9097     if (hdc) 
9098         LISTVIEW_Refresh(infoPtr, hdc, NULL);
9099     else
9100     {
9101         PAINTSTRUCT ps;
9102
9103         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9104         if (!hdc) return 1;
9105         LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9106         EndPaint(infoPtr->hwndSelf, &ps);
9107     }
9108
9109     return 0;
9110 }
9111
9112
9113 /***
9114  * DESCRIPTION:
9115  * Paints/Repaints the listview control.
9116  *
9117  * PARAMETER(S):
9118  * [I] infoPtr : valid pointer to the listview structure
9119  * [I] hdc : device context handle
9120  * [I] options : drawing options
9121  *
9122  * RETURN:
9123  * Zero
9124  */
9125 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9126 {
9127     FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9128
9129     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9130         return 0;
9131
9132     if (options & PRF_ERASEBKGND)
9133         LISTVIEW_EraseBkgnd(infoPtr, hdc);
9134
9135     if (options & PRF_CLIENT)
9136         LISTVIEW_Paint(infoPtr, hdc);
9137
9138     return 0;
9139 }
9140
9141
9142 /***
9143  * DESCRIPTION:
9144  * Processes double click messages (right mouse button).
9145  *
9146  * PARAMETER(S):
9147  * [I] infoPtr : valid pointer to the listview structure
9148  * [I] wKey : key flag
9149  * [I] x,y : mouse coordinate
9150  *
9151  * RETURN:
9152  * Zero
9153  */
9154 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9155 {
9156     LVHITTESTINFO lvHitTestInfo;
9157     
9158     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9159
9160     /* send NM_RELEASEDCAPTURE notification */
9161     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9162
9163     /* send NM_RDBLCLK notification */
9164     lvHitTestInfo.pt.x = x;
9165     lvHitTestInfo.pt.y = y;
9166     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9167     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9168
9169     return 0;
9170 }
9171
9172 /***
9173  * DESCRIPTION:
9174  * Processes mouse down messages (right mouse button).
9175  *
9176  * PARAMETER(S):
9177  * [I] infoPtr : valid pointer to the listview structure
9178  * [I] wKey : key flag
9179  * [I] x,y : mouse coordinate
9180  *
9181  * RETURN:
9182  * Zero
9183  */
9184 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9185 {
9186     LVHITTESTINFO lvHitTestInfo;
9187     INT nItem;
9188
9189     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9190
9191     /* send NM_RELEASEDCAPTURE notification */
9192     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9193
9194     /* make sure the listview control window has the focus */
9195     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9196
9197     /* set right button down flag */
9198     infoPtr->bRButtonDown = TRUE;
9199
9200     /* determine the index of the selected item */
9201     lvHitTestInfo.pt.x = x;
9202     lvHitTestInfo.pt.y = y;
9203     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9204   
9205     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9206     {
9207         LISTVIEW_SetItemFocus(infoPtr, nItem);
9208         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9209             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9210             LISTVIEW_SetSelection(infoPtr, nItem);
9211     }
9212     else
9213     {
9214         LISTVIEW_DeselectAll(infoPtr);
9215     }
9216
9217     return 0;
9218 }
9219
9220 /***
9221  * DESCRIPTION:
9222  * Processes mouse up messages (right mouse button).
9223  *
9224  * PARAMETER(S):
9225  * [I] infoPtr : valid pointer to the listview structure
9226  * [I] wKey : key flag
9227  * [I] x,y : mouse coordinate
9228  *
9229  * RETURN:
9230  * Zero
9231  */
9232 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9233 {
9234     LVHITTESTINFO lvHitTestInfo;
9235     POINT pt;
9236
9237     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9238
9239     if (!infoPtr->bRButtonDown) return 0;
9240  
9241     /* set button flag */
9242     infoPtr->bRButtonDown = FALSE;
9243
9244     /* Send NM_RClICK notification */
9245     lvHitTestInfo.pt.x = x;
9246     lvHitTestInfo.pt.y = y;
9247     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9248     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9249
9250     /* Change to screen coordinate for WM_CONTEXTMENU */
9251     pt = lvHitTestInfo.pt;
9252     ClientToScreen(infoPtr->hwndSelf, &pt);
9253
9254     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9255     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9256                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9257
9258     return 0;
9259 }
9260
9261
9262 /***
9263  * DESCRIPTION:
9264  * Sets the cursor.
9265  *
9266  * PARAMETER(S):
9267  * [I] infoPtr : valid pointer to the listview structure
9268  * [I] hwnd : window handle of window containing the cursor
9269  * [I] nHittest : hit-test code
9270  * [I] wMouseMsg : ideintifier of the mouse message
9271  *
9272  * RETURN:
9273  * TRUE if cursor is set
9274  * FALSE otherwise
9275  */
9276 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9277 {
9278     LVHITTESTINFO lvHitTestInfo;
9279
9280     if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9281
9282     if(!infoPtr->hHotCursor)  return FALSE;
9283
9284     GetCursorPos(&lvHitTestInfo.pt);
9285     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9286
9287     SetCursor(infoPtr->hHotCursor);
9288
9289     return TRUE;
9290 }
9291
9292 /***
9293  * DESCRIPTION:
9294  * Sets the focus.
9295  *
9296  * PARAMETER(S):
9297  * [I] infoPtr : valid pointer to the listview structure
9298  * [I] hwndLoseFocus : handle of previously focused window
9299  *
9300  * RETURN:
9301  * Zero
9302  */
9303 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9304 {
9305     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9306
9307     /* if we have the focus already, there's nothing to do */
9308     if (infoPtr->bFocus) return 0;
9309    
9310     /* send NM_SETFOCUS notification */
9311     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9312
9313     /* set window focus flag */
9314     infoPtr->bFocus = TRUE;
9315
9316     /* put the focus rect back on */
9317     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9318
9319     /* redraw all visible selected items */
9320     LISTVIEW_InvalidateSelectedItems(infoPtr);
9321
9322     return 0;
9323 }
9324
9325 /***
9326  * DESCRIPTION:
9327  * Sets the font.
9328  *
9329  * PARAMETER(S):
9330  * [I] infoPtr : valid pointer to the listview structure
9331  * [I] fRedraw : font handle
9332  * [I] fRedraw : redraw flag
9333  *
9334  * RETURN:
9335  * Zero
9336  */
9337 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9338 {
9339     HFONT oldFont = infoPtr->hFont;
9340
9341     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9342
9343     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9344     if (infoPtr->hFont == oldFont) return 0;
9345     
9346     LISTVIEW_SaveTextMetrics(infoPtr);
9347
9348     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9349     {
9350         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9351         LISTVIEW_UpdateSize(infoPtr);
9352         LISTVIEW_UpdateScroll(infoPtr);
9353     }
9354
9355     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9356
9357     return 0;
9358 }
9359
9360 /***
9361  * DESCRIPTION:
9362  * Message handling for WM_SETREDRAW.
9363  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9364  *
9365  * PARAMETER(S):
9366  * [I] infoPtr : valid pointer to the listview structure
9367  * [I] bRedraw: state of redraw flag
9368  *
9369  * RETURN:
9370  * DefWinProc return value
9371  */
9372 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9373 {
9374     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9375
9376     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9377     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9378
9379     infoPtr->bRedraw = bRedraw;
9380
9381     if(!bRedraw) return 0;
9382     
9383     if (is_autoarrange(infoPtr))
9384         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9385     LISTVIEW_UpdateScroll(infoPtr);
9386
9387     /* despite what the WM_SETREDRAW docs says, apps expect us
9388      * to invalidate the listview here... stupid! */
9389     LISTVIEW_InvalidateList(infoPtr);
9390
9391     return 0;
9392 }
9393
9394 /***
9395  * DESCRIPTION:
9396  * Resizes the listview control. This function processes WM_SIZE
9397  * messages.  At this time, the width and height are not used.
9398  *
9399  * PARAMETER(S):
9400  * [I] infoPtr : valid pointer to the listview structure
9401  * [I] Width : new width
9402  * [I] Height : new height
9403  *
9404  * RETURN:
9405  * Zero
9406  */
9407 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9408 {
9409     RECT rcOld = infoPtr->rcList;
9410
9411     TRACE("(width=%d, height=%d)\n", Width, Height);
9412
9413     LISTVIEW_UpdateSize(infoPtr);
9414     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9415   
9416     /* do not bother with display related stuff if we're not redrawing */ 
9417     if (!is_redrawing(infoPtr)) return 0;
9418     
9419     if (is_autoarrange(infoPtr)) 
9420         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9421
9422     LISTVIEW_UpdateScroll(infoPtr);
9423
9424     /* refresh all only for lists whose height changed significantly */
9425     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
9426         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9427         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9428         LISTVIEW_InvalidateList(infoPtr);
9429
9430   return 0;
9431 }
9432
9433 /***
9434  * DESCRIPTION:
9435  * Sets the size information.
9436  *
9437  * PARAMETER(S):
9438  * [I] infoPtr : valid pointer to the listview structure
9439  *
9440  * RETURN:
9441  *  None
9442  */
9443 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9444 {
9445     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9446
9447     TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9448     
9449     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9450
9451     if (uView == LVS_LIST)
9452     {
9453         /* Apparently the "LIST" style is supposed to have the same
9454          * number of items in a column even if there is no scroll bar.
9455          * Since if a scroll bar already exists then the bottom is already
9456          * reduced, only reduce if the scroll bar does not currently exist.
9457          * The "2" is there to mimic the native control. I think it may be
9458          * related to either padding or edges.  (GLA 7/2002)
9459          */
9460         if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9461             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9462         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9463     }
9464     else if (uView == LVS_REPORT)
9465     {
9466         HDLAYOUT hl;
9467         WINDOWPOS wp;
9468
9469         hl.prc = &infoPtr->rcList;
9470         hl.pwpos = &wp;
9471         SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9472         TRACE("  wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9473         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9474                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9475                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9476         TRACE("  after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9477
9478         infoPtr->rcList.top = max(wp.cy, 0);
9479         infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9480     }
9481
9482     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9483 }
9484
9485 /***
9486  * DESCRIPTION:
9487  * Processes WM_STYLECHANGED messages.
9488  *
9489  * PARAMETER(S):
9490  * [I] infoPtr : valid pointer to the listview structure
9491  * [I] wStyleType : window style type (normal or extended)
9492  * [I] lpss : window style information
9493  *
9494  * RETURN:
9495  * Zero
9496  */
9497 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9498                                  const STYLESTRUCT *lpss)
9499 {
9500     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9501     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9502     UINT style;
9503
9504     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9505           wStyleType, lpss->styleOld, lpss->styleNew);
9506
9507     if (wStyleType != GWL_STYLE) return 0;
9508   
9509     /* FIXME: if LVS_NOSORTHEADER changed, update header */
9510     /*        what if LVS_OWNERDATA changed? */
9511     /*        or LVS_SINGLESEL */
9512     /*        or LVS_SORT{AS,DES}CENDING */
9513
9514     infoPtr->dwStyle = lpss->styleNew;
9515
9516     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9517         ((lpss->styleNew & WS_HSCROLL) == 0))
9518        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9519
9520     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9521         ((lpss->styleNew & WS_VSCROLL) == 0))
9522        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9523
9524     if (uNewView != uOldView)
9525     {
9526         SIZE oldIconSize = infoPtr->iconSize;
9527         HIMAGELIST himl;
9528     
9529         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9530         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9531
9532         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9533         SetRectEmpty(&infoPtr->rcFocus);
9534
9535         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9536         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9537     
9538         if (uNewView == LVS_ICON)
9539         {
9540             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9541             {
9542                 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9543                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9544                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9545             }
9546         }
9547         else if (uNewView == LVS_REPORT)
9548         {
9549             HDLAYOUT hl;
9550             WINDOWPOS wp;
9551
9552             hl.prc = &infoPtr->rcList;
9553             hl.pwpos = &wp;
9554             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9555             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9556                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9557                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9558         }
9559
9560         LISTVIEW_UpdateItemSize(infoPtr);
9561     }
9562
9563     if (uNewView == LVS_REPORT)
9564     {
9565         if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9566         {
9567             if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9568             {
9569                 /* Turn off the header control */
9570                 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9571                 TRACE("Hide header control, was 0x%08x\n", style);
9572                 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9573             } else {
9574                 /* Turn on the header control */
9575                 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9576                 {
9577                     TRACE("Show header control, was 0x%08x\n", style);
9578                     SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9579                 }
9580             }
9581         }
9582     }
9583
9584     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9585          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9586          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9587
9588     /* update the size of the client area */
9589     LISTVIEW_UpdateSize(infoPtr);
9590
9591     /* add scrollbars if needed */
9592     LISTVIEW_UpdateScroll(infoPtr);
9593
9594     /* invalidate client area + erase background */
9595     LISTVIEW_InvalidateList(infoPtr);
9596
9597     return 0;
9598 }
9599
9600 /***
9601  * DESCRIPTION:
9602  * Window procedure of the listview control.
9603  *
9604  */
9605 static LRESULT WINAPI
9606 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9607 {
9608   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9609
9610   TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9611
9612   if (!infoPtr && (uMsg != WM_NCCREATE))
9613     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9614
9615   switch (uMsg)
9616   {
9617   case LVM_APPROXIMATEVIEWRECT:
9618     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9619                                         LOWORD(lParam), HIWORD(lParam));
9620   case LVM_ARRANGE:
9621     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9622
9623 /* case LVM_CANCELEDITLABEL: */
9624
9625   case LVM_CREATEDRAGIMAGE:
9626     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9627
9628   case LVM_DELETEALLITEMS:
9629     return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9630
9631   case LVM_DELETECOLUMN:
9632     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9633
9634   case LVM_DELETEITEM:
9635     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9636
9637   case LVM_EDITLABELW:
9638     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9639
9640   case LVM_EDITLABELA:
9641     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9642
9643   /* case LVM_ENABLEGROUPVIEW: */
9644
9645   case LVM_ENSUREVISIBLE:
9646     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9647
9648   case LVM_FINDITEMW:
9649     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9650
9651   case LVM_FINDITEMA:
9652     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9653
9654   case LVM_GETBKCOLOR:
9655     return infoPtr->clrBk;
9656
9657   /* case LVM_GETBKIMAGE: */
9658
9659   case LVM_GETCALLBACKMASK:
9660     return infoPtr->uCallbackMask;
9661
9662   case LVM_GETCOLUMNA:
9663     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9664
9665   case LVM_GETCOLUMNW:
9666     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9667
9668   case LVM_GETCOLUMNORDERARRAY:
9669     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9670
9671   case LVM_GETCOLUMNWIDTH:
9672     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9673
9674   case LVM_GETCOUNTPERPAGE:
9675     return LISTVIEW_GetCountPerPage(infoPtr);
9676
9677   case LVM_GETEDITCONTROL:
9678     return (LRESULT)infoPtr->hwndEdit;
9679
9680   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9681     return infoPtr->dwLvExStyle;
9682
9683   /* case LVM_GETGROUPINFO: */
9684
9685   /* case LVM_GETGROUPMETRICS: */
9686
9687   case LVM_GETHEADER:
9688     return (LRESULT)infoPtr->hwndHeader;
9689
9690   case LVM_GETHOTCURSOR:
9691     return (LRESULT)infoPtr->hHotCursor;
9692
9693   case LVM_GETHOTITEM:
9694     return infoPtr->nHotItem;
9695
9696   case LVM_GETHOVERTIME:
9697     return infoPtr->dwHoverTime;
9698
9699   case LVM_GETIMAGELIST:
9700     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9701
9702   /* case LVM_GETINSERTMARK: */
9703
9704   /* case LVM_GETINSERTMARKCOLOR: */
9705
9706   /* case LVM_GETINSERTMARKRECT: */
9707
9708   case LVM_GETISEARCHSTRINGA:
9709   case LVM_GETISEARCHSTRINGW:
9710     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9711     return FALSE;
9712
9713   case LVM_GETITEMA:
9714     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9715
9716   case LVM_GETITEMW:
9717     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9718
9719   case LVM_GETITEMCOUNT:
9720     return infoPtr->nItemCount;
9721
9722   case LVM_GETITEMPOSITION:
9723     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9724
9725   case LVM_GETITEMRECT:
9726     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9727
9728   case LVM_GETITEMSPACING:
9729     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9730
9731   case LVM_GETITEMSTATE:
9732     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9733
9734   case LVM_GETITEMTEXTA:
9735     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9736
9737   case LVM_GETITEMTEXTW:
9738     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9739
9740   case LVM_GETNEXTITEM:
9741     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9742
9743   case LVM_GETNUMBEROFWORKAREAS:
9744     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9745     return 1;
9746
9747   case LVM_GETORIGIN:
9748     if (!lParam) return FALSE;
9749     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9750         (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9751     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9752     return TRUE;
9753
9754   /* case LVM_GETOUTLINECOLOR: */
9755
9756   /* case LVM_GETSELECTEDCOLUMN: */
9757
9758   case LVM_GETSELECTEDCOUNT:
9759     return LISTVIEW_GetSelectedCount(infoPtr);
9760
9761   case LVM_GETSELECTIONMARK:
9762     return infoPtr->nSelectionMark;
9763
9764   case LVM_GETSTRINGWIDTHA:
9765     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9766
9767   case LVM_GETSTRINGWIDTHW:
9768     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9769
9770   case LVM_GETSUBITEMRECT:
9771     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9772
9773   case LVM_GETTEXTBKCOLOR:
9774     return infoPtr->clrTextBk;
9775
9776   case LVM_GETTEXTCOLOR:
9777     return infoPtr->clrText;
9778
9779   /* case LVM_GETTILEINFO: */
9780
9781   /* case LVM_GETTILEVIEWINFO: */
9782
9783   case LVM_GETTOOLTIPS:
9784     if( !infoPtr->hwndToolTip )
9785         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9786     return (LRESULT)infoPtr->hwndToolTip;
9787
9788   case LVM_GETTOPINDEX:
9789     return LISTVIEW_GetTopIndex(infoPtr);
9790
9791   case LVM_GETUNICODEFORMAT:
9792     return (infoPtr->notifyFormat == NFR_UNICODE);
9793
9794   /* case LVM_GETVIEW: */
9795
9796   case LVM_GETVIEWRECT:
9797     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9798
9799   case LVM_GETWORKAREAS:
9800     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9801     return FALSE;
9802
9803   /* case LVM_HASGROUP: */
9804
9805   case LVM_HITTEST:
9806     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9807
9808   case LVM_INSERTCOLUMNA:
9809     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9810
9811   case LVM_INSERTCOLUMNW:
9812     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9813
9814   /* case LVM_INSERTGROUP: */
9815
9816   /* case LVM_INSERTGROUPSORTED: */
9817
9818   case LVM_INSERTITEMA:
9819     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9820
9821   case LVM_INSERTITEMW:
9822     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9823
9824   /* case LVM_INSERTMARKHITTEST: */
9825
9826   /* case LVM_ISGROUPVIEWENABLED: */
9827
9828   /* case LVM_MAPIDTOINDEX: */
9829
9830   /* case LVM_MAPINDEXTOID: */
9831
9832   /* case LVM_MOVEGROUP: */
9833
9834   /* case LVM_MOVEITEMTOGROUP: */
9835
9836   case LVM_REDRAWITEMS:
9837     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9838
9839   /* case LVM_REMOVEALLGROUPS: */
9840
9841   /* case LVM_REMOVEGROUP: */
9842
9843   case LVM_SCROLL:
9844     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9845
9846   case LVM_SETBKCOLOR:
9847     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9848
9849   /* case LVM_SETBKIMAGE: */
9850
9851   case LVM_SETCALLBACKMASK:
9852     infoPtr->uCallbackMask = (UINT)wParam;
9853     return TRUE;
9854
9855   case LVM_SETCOLUMNA:
9856     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9857
9858   case LVM_SETCOLUMNW:
9859     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9860
9861   case LVM_SETCOLUMNORDERARRAY:
9862     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9863
9864   case LVM_SETCOLUMNWIDTH:
9865     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9866
9867   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9868     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9869
9870   /* case LVM_SETGROUPINFO: */
9871
9872   /* case LVM_SETGROUPMETRICS: */
9873
9874   case LVM_SETHOTCURSOR:
9875     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9876
9877   case LVM_SETHOTITEM:
9878     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9879
9880   case LVM_SETHOVERTIME:
9881     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9882
9883   case LVM_SETICONSPACING:
9884     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9885
9886   case LVM_SETIMAGELIST:
9887     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9888
9889   /* case LVM_SETINFOTIP: */
9890
9891   /* case LVM_SETINSERTMARK: */
9892
9893   /* case LVM_SETINSERTMARKCOLOR: */
9894
9895   case LVM_SETITEMA:
9896     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9897
9898   case LVM_SETITEMW:
9899     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9900
9901   case LVM_SETITEMCOUNT:
9902     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9903
9904   case LVM_SETITEMPOSITION:
9905     {
9906         POINT pt;
9907         pt.x = (short)LOWORD(lParam);
9908         pt.y = (short)HIWORD(lParam);
9909         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9910     }
9911
9912   case LVM_SETITEMPOSITION32:
9913     if (lParam == 0) return FALSE;
9914     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9915
9916   case LVM_SETITEMSTATE:
9917     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9918
9919   case LVM_SETITEMTEXTA:
9920     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9921
9922   case LVM_SETITEMTEXTW:
9923     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9924
9925   /* case LVM_SETOUTLINECOLOR: */
9926
9927   /* case LVM_SETSELECTEDCOLUMN: */
9928
9929   case LVM_SETSELECTIONMARK:
9930     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9931
9932   case LVM_SETTEXTBKCOLOR:
9933     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9934
9935   case LVM_SETTEXTCOLOR:
9936     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9937
9938   /* case LVM_SETTILEINFO: */
9939
9940   /* case LVM_SETTILEVIEWINFO: */
9941
9942   /* case LVM_SETTILEWIDTH: */
9943
9944   case LVM_SETTOOLTIPS:
9945     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9946
9947   case LVM_SETUNICODEFORMAT:
9948     return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9949
9950   /* case LVM_SETVIEW: */
9951
9952   /* case LVM_SETWORKAREAS: */
9953
9954   /* case LVM_SORTGROUPS: */
9955
9956   case LVM_SORTITEMS:
9957     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9958
9959   /* LVM_SORTITEMSEX: */
9960
9961   case LVM_SUBITEMHITTEST:
9962     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9963
9964   case LVM_UPDATE:
9965     return LISTVIEW_Update(infoPtr, (INT)wParam);
9966
9967   case WM_CHAR:
9968     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9969
9970   case WM_COMMAND:
9971     return LISTVIEW_Command(infoPtr, wParam, lParam);
9972
9973   case WM_NCCREATE:
9974     return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9975
9976   case WM_CREATE:
9977     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9978
9979   case WM_DESTROY:
9980     return LISTVIEW_Destroy(infoPtr);
9981
9982   case WM_ENABLE:
9983     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9984
9985   case WM_ERASEBKGND:
9986     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9987
9988   case WM_GETDLGCODE:
9989     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9990
9991   case WM_GETFONT:
9992     return (LRESULT)infoPtr->hFont;
9993
9994   case WM_HSCROLL:
9995     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9996
9997   case WM_KEYDOWN:
9998     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9999
10000   case WM_KILLFOCUS:
10001     return LISTVIEW_KillFocus(infoPtr);
10002
10003   case WM_LBUTTONDBLCLK:
10004     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10005
10006   case WM_LBUTTONDOWN:
10007     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10008
10009   case WM_LBUTTONUP:
10010     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10011
10012   case WM_MOUSEMOVE:
10013     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10014
10015   case WM_MOUSEHOVER:
10016     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10017
10018   case WM_NCDESTROY:
10019     return LISTVIEW_NCDestroy(infoPtr);
10020
10021   case WM_NCPAINT:
10022     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10023         return 0;
10024     goto fwd_msg;
10025
10026   case WM_NOTIFY:
10027     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10028         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10029     else return 0;
10030
10031   case WM_NOTIFYFORMAT:
10032     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10033
10034   case WM_PRINTCLIENT:
10035     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10036
10037   case WM_PAINT:
10038     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10039
10040   case WM_RBUTTONDBLCLK:
10041     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10042
10043   case WM_RBUTTONDOWN:
10044     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10045
10046   case WM_RBUTTONUP:
10047     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10048
10049   case WM_SETCURSOR:
10050     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10051       return TRUE;
10052     goto fwd_msg;
10053
10054   case WM_SETFOCUS:
10055     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10056
10057   case WM_SETFONT:
10058     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10059
10060   case WM_SETREDRAW:
10061     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10062
10063   case WM_SIZE:
10064     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10065
10066   case WM_STYLECHANGED:
10067     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10068
10069   case WM_SYSCOLORCHANGE:
10070     COMCTL32_RefreshSysColors();
10071     return 0;
10072
10073 /*      case WM_TIMER: */
10074   case WM_THEMECHANGED:
10075     return LISTVIEW_ThemeChanged(infoPtr);
10076
10077   case WM_VSCROLL:
10078     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10079
10080   case WM_MOUSEWHEEL:
10081       if (wParam & (MK_SHIFT | MK_CONTROL))
10082           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10083       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10084
10085   case WM_WINDOWPOSCHANGED:
10086       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
10087       {
10088       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10089           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10090                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10091
10092       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10093       {
10094           MEASUREITEMSTRUCT mis;
10095           mis.CtlType = ODT_LISTVIEW;
10096           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10097           mis.itemID = -1;
10098           mis.itemWidth = 0;
10099           mis.itemData = 0;
10100           mis.itemHeight= infoPtr->nItemHeight;
10101           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10102           if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10103               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10104       }
10105
10106           LISTVIEW_UpdateSize(infoPtr);
10107           LISTVIEW_UpdateScroll(infoPtr);
10108       }
10109       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10110
10111 /*      case WM_WININICHANGE: */
10112
10113   default:
10114     if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10115       ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10116
10117   fwd_msg:
10118     /* call default window procedure */
10119     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10120   }
10121
10122 }
10123
10124 /***
10125  * DESCRIPTION:
10126  * Registers the window class.
10127  *
10128  * PARAMETER(S):
10129  * None
10130  *
10131  * RETURN:
10132  * None
10133  */
10134 void LISTVIEW_Register(void)
10135 {
10136     WNDCLASSW wndClass;
10137
10138     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10139     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10140     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10141     wndClass.cbClsExtra = 0;
10142     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10143     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10144     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10145     wndClass.lpszClassName = WC_LISTVIEWW;
10146     RegisterClassW(&wndClass);
10147 }
10148
10149 /***
10150  * DESCRIPTION:
10151  * Unregisters the window class.
10152  *
10153  * PARAMETER(S):
10154  * None
10155  *
10156  * RETURN:
10157  * None
10158  */
10159 void LISTVIEW_Unregister(void)
10160 {
10161     UnregisterClassW(WC_LISTVIEWW, NULL);
10162 }
10163
10164 /***
10165  * DESCRIPTION:
10166  * Handle any WM_COMMAND messages
10167  *
10168  * PARAMETER(S):
10169  * [I] infoPtr : valid pointer to the listview structure
10170  * [I] wParam : the first message parameter
10171  * [I] lParam : the second message parameter
10172  *
10173  * RETURN:
10174  *   Zero.
10175  */
10176 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10177 {
10178     switch (HIWORD(wParam))
10179     {
10180         case EN_UPDATE:
10181         {
10182             /*
10183              * Adjust the edit window size
10184              */
10185             WCHAR buffer[1024];
10186             HDC           hdc = GetDC(infoPtr->hwndEdit);
10187             HFONT         hFont, hOldFont = 0;
10188             RECT          rect;
10189             SIZE          sz;
10190
10191             if (!infoPtr->hwndEdit || !hdc) return 0;
10192             GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10193             GetWindowRect(infoPtr->hwndEdit, &rect);
10194
10195             /* Select font to get the right dimension of the string */
10196             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10197             if(hFont != 0)
10198             {
10199                 hOldFont = SelectObject(hdc, hFont);
10200             }
10201
10202             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10203             {
10204                 TEXTMETRICW textMetric;
10205
10206                 /* Add Extra spacing for the next character */
10207                 GetTextMetricsW(hdc, &textMetric);
10208                 sz.cx += (textMetric.tmMaxCharWidth * 2);
10209
10210                 SetWindowPos (
10211                     infoPtr->hwndEdit,
10212                     HWND_TOP,
10213                     0,
10214                     0,
10215                     sz.cx,
10216                     rect.bottom - rect.top,
10217                     SWP_DRAWFRAME|SWP_NOMOVE);
10218             }
10219             if(hFont != 0)
10220                 SelectObject(hdc, hOldFont);
10221
10222             ReleaseDC(infoPtr->hwndEdit, hdc);
10223
10224             break;
10225         }
10226
10227         default:
10228           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10229     }
10230
10231     return 0;
10232 }
10233
10234
10235 /***
10236  * DESCRIPTION:
10237  * Subclassed edit control windproc function
10238  *
10239  * PARAMETER(S):
10240  * [I] hwnd : the edit window handle
10241  * [I] uMsg : the message that is to be processed
10242  * [I] wParam : first message parameter
10243  * [I] lParam : second message parameter
10244  * [I] isW : TRUE if input is Unicode
10245  *
10246  * RETURN:
10247  *   Zero.
10248  */
10249 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10250 {
10251     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10252     BOOL cancel = FALSE;
10253
10254     TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10255           hwnd, uMsg, wParam, lParam, isW);
10256
10257     switch (uMsg)
10258     {
10259         case WM_GETDLGCODE:
10260           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10261
10262         case WM_KILLFOCUS:
10263             break;
10264
10265         case WM_DESTROY:
10266         {
10267             WNDPROC editProc = infoPtr->EditWndProc;
10268             infoPtr->EditWndProc = 0;
10269             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10270             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10271         }
10272
10273         case WM_KEYDOWN:
10274             if (VK_ESCAPE == (INT)wParam)
10275             {
10276                 cancel = TRUE;
10277                 break;
10278             }
10279             else if (VK_RETURN == (INT)wParam)
10280                 break;
10281
10282         default:
10283             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10284     }
10285
10286     /* kill the edit */
10287     if (infoPtr->hwndEdit)
10288     {
10289         LPWSTR buffer = NULL;
10290
10291         infoPtr->hwndEdit = 0;
10292         if (!cancel)
10293         {
10294             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10295
10296             if (len)
10297             {
10298                 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10299                 {
10300                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
10301                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10302                 }
10303             }
10304         }
10305         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10306
10307         Free(buffer);
10308     }
10309
10310     SendMessageW(hwnd, WM_CLOSE, 0, 0);
10311     return 0;
10312 }
10313
10314 /***
10315  * DESCRIPTION:
10316  * Subclassed edit control Unicode windproc function
10317  *
10318  * PARAMETER(S):
10319  * [I] hwnd : the edit window handle
10320  * [I] uMsg : the message that is to be processed
10321  * [I] wParam : first message parameter
10322  * [I] lParam : second message parameter
10323  *
10324  * RETURN:
10325  */
10326 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10327 {
10328     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10329 }
10330
10331 /***
10332  * DESCRIPTION:
10333  * Subclassed edit control ANSI windproc function
10334  *
10335  * PARAMETER(S):
10336  * [I] hwnd : the edit window handle
10337  * [I] uMsg : the message that is to be processed
10338  * [I] wParam : first message parameter
10339  * [I] lParam : second message parameter
10340  *
10341  * RETURN:
10342  */
10343 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10344 {
10345     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10346 }
10347
10348 /***
10349  * DESCRIPTION:
10350  * Creates a subclassed edit control
10351  *
10352  * PARAMETER(S):
10353  * [I] infoPtr : valid pointer to the listview structure
10354  * [I] text : initial text for the edit
10355  * [I] style : the window style
10356  * [I] isW : TRUE if input is Unicode
10357  *
10358  * RETURN:
10359  */
10360 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10361         INT x, INT y, INT width, INT height, BOOL isW)
10362 {
10363     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10364     HWND hedit;
10365     SIZE sz;
10366     HDC hdc;
10367     HDC hOldFont=0;
10368     TEXTMETRICW textMetric;
10369     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10370
10371     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10372
10373     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10374     hdc = GetDC(infoPtr->hwndSelf);
10375
10376     /* Select the font to get appropriate metric dimensions */
10377     if(infoPtr->hFont != 0)
10378         hOldFont = SelectObject(hdc, infoPtr->hFont);
10379
10380     /*Get String Length in pixels */
10381     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10382
10383     /*Add Extra spacing for the next character */
10384     GetTextMetricsW(hdc, &textMetric);
10385     sz.cx += (textMetric.tmMaxCharWidth * 2);
10386
10387     if(infoPtr->hFont != 0)
10388         SelectObject(hdc, hOldFont);
10389
10390     ReleaseDC(infoPtr->hwndSelf, hdc);
10391     if (isW)
10392         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10393     else
10394         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10395
10396     if (!hedit) return 0;
10397
10398     infoPtr->EditWndProc = (WNDPROC)
10399         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10400                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10401
10402     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10403
10404     return hedit;
10405 }