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