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