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