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