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