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