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