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