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