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