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