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