kernel: Add some more tests for FindFirstChangeNotification.
[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
3681     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3682
3683     /* get information needed for drawing the item */
3684     lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3685     if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3686     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3687     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3688     lvItem.iItem = nItem;
3689     lvItem.iSubItem = nSubItem;
3690     lvItem.state = 0;
3691     lvItem.lParam = 0;
3692     lvItem.cchTextMax = DISP_TEXT_SIZE;
3693     lvItem.pszText = szDispText;
3694     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3695     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
3696         lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3697     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3698     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3699
3700     /* now check if we need to update the focus rectangle */
3701     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3702     
3703     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3704     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3705     OffsetRect(&rcBox, pos.x, pos.y);
3706     OffsetRect(&rcState, pos.x, pos.y);
3707     OffsetRect(&rcIcon, pos.x, pos.y);
3708     OffsetRect(&rcLabel, pos.x, pos.y);
3709     TRACE("    rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n", 
3710         wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcState),
3711         wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3712
3713     /* fill in the custom draw structure */
3714     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3715
3716     if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3717     if (cdmode & CDRF_NOTIFYITEMDRAW)
3718         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3719     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3720     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3721     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3722     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3723     {
3724         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3725         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3726     }
3727     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3728         prepaint_setup(infoPtr, hdc, &nmlvcd);
3729
3730     /* in full row select, subitems, will just use main item's colors */
3731     if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3732         nmlvcd.clrTextBk = CLR_NONE;
3733     
3734     /* state icons */
3735     if (infoPtr->himlState && !IsRectEmpty(&rcState))
3736     {
3737         UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3738         if (uStateImage)
3739         {
3740              TRACE("uStateImage=%d\n", uStateImage);
3741              ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3742         }
3743     }
3744
3745     /* small icons */
3746     himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3747     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3748     {
3749         TRACE("iImage=%d\n", lvItem.iImage);
3750         ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3751                         (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3752     }
3753
3754     /* Don't bother painting item being edited */
3755     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3756
3757     /* draw the selection background, if we're drawing the main item */
3758     if (nSubItem == 0)
3759     {
3760         rcSelect = rcLabel;
3761         if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3762             rcSelect.right = rcBox.right;
3763    
3764         if (nmlvcd.clrTextBk != CLR_NONE) 
3765             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3766         if(lprcFocus) *lprcFocus = rcSelect;
3767     }
3768    
3769     /* figure out the text drawing flags */
3770     uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3771     if (uView == LVS_ICON)
3772         uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3773     else if (nSubItem)
3774     {
3775         switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3776         {
3777         case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
3778         case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3779         default:            uFormat |= DT_LEFT;
3780         }
3781     }
3782     if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3783     {
3784         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3785         else rcLabel.left += LABEL_HOR_PADDING;
3786     }
3787     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3788     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3789
3790 postpaint:
3791     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3792         notify_postpaint(infoPtr, &nmlvcd);
3793     return TRUE;
3794 }
3795
3796 /***
3797  * DESCRIPTION:
3798  * Draws listview items when in owner draw mode.
3799  *
3800  * PARAMETER(S):
3801  * [I] infoPtr : valid pointer to the listview structure
3802  * [I] hdc : device context handle
3803  *
3804  * RETURN:
3805  * None
3806  */
3807 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3808 {
3809     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3810     DWORD cditemmode = CDRF_DODEFAULT;
3811     NMLVCUSTOMDRAW nmlvcd;
3812     POINT Origin, Position;
3813     DRAWITEMSTRUCT dis;
3814     LVITEMW item;
3815     
3816     TRACE("()\n");
3817
3818     ZeroMemory(&dis, sizeof(dis));
3819     
3820     /* Get scroll info once before loop */
3821     LISTVIEW_GetOrigin(infoPtr, &Origin);
3822     
3823     /* iterate through the invalidated rows */
3824     while(iterator_next(i))
3825     {
3826         item.iItem = i->nItem;
3827         item.iSubItem = 0;
3828         item.mask = LVIF_PARAM | LVIF_STATE;
3829         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3830         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3831            
3832         dis.CtlType = ODT_LISTVIEW;
3833         dis.CtlID = uID;
3834         dis.itemID = item.iItem;
3835         dis.itemAction = ODA_DRAWENTIRE;
3836         dis.itemState = 0;
3837         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3838         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3839         dis.hwndItem = infoPtr->hwndSelf;
3840         dis.hDC = hdc;
3841         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3842         dis.rcItem.left = Position.x + Origin.x;
3843         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3844         dis.rcItem.top = Position.y + Origin.y;
3845         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3846         dis.itemData = item.lParam;
3847
3848         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3849
3850     /*
3851      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3852      * structure for the rest. of the paint cycle
3853      */
3854         customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3855         if (cdmode & CDRF_NOTIFYITEMDRAW)
3856             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3857     
3858         if (!(cditemmode & CDRF_SKIPDEFAULT))
3859         {
3860             prepaint_setup (infoPtr, hdc, &nmlvcd);
3861             SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3862         }
3863
3864         if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3865             notify_postpaint(infoPtr, &nmlvcd);
3866     }
3867 }
3868
3869 /***
3870  * DESCRIPTION:
3871  * Draws listview items when in report display mode.
3872  *
3873  * PARAMETER(S):
3874  * [I] infoPtr : valid pointer to the listview structure
3875  * [I] hdc : device context handle
3876  * [I] cdmode : custom draw mode
3877  *
3878  * RETURN:
3879  * None
3880  */
3881 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3882 {
3883     INT rgntype;
3884     RECT rcClip, rcItem;
3885     POINT Origin, Position;
3886     RANGE colRange;
3887     ITERATOR j;
3888
3889     TRACE("()\n");
3890
3891     /* figure out what to draw */
3892     rgntype = GetClipBox(hdc, &rcClip);
3893     if (rgntype == NULLREGION) return;
3894     
3895     /* Get scroll info once before loop */
3896     LISTVIEW_GetOrigin(infoPtr, &Origin);
3897     
3898     /* narrow down the columns we need to paint */
3899     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3900     {
3901         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3902         if (rcItem.right + Origin.x >= rcClip.left) break;
3903     }
3904     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3905     {
3906         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3907         if (rcItem.left + Origin.x < rcClip.right) break;
3908     }
3909     iterator_rangeitems(&j, colRange);
3910
3911     /* in full row select, we _have_ to draw the main item */
3912     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3913         j.nSpecial = 0;
3914
3915     /* iterate through the invalidated rows */
3916     while(iterator_next(i))
3917     {
3918         /* iterate through the invalidated columns */
3919         while(iterator_next(&j))
3920         {
3921             LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3922             Position.x += Origin.x;
3923             Position.y += Origin.y;
3924
3925             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3926             {
3927                 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3928                 rcItem.top = 0;
3929                 rcItem.bottom = infoPtr->nItemHeight;
3930                 OffsetRect(&rcItem, Position.x, Position.y);
3931                 if (!RectVisible(hdc, &rcItem)) continue;
3932             }
3933
3934             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3935         }
3936     }
3937     iterator_destroy(&j);
3938 }
3939
3940 /***
3941  * DESCRIPTION:
3942  * Draws listview items when in list display mode.
3943  *
3944  * PARAMETER(S):
3945  * [I] infoPtr : valid pointer to the listview structure
3946  * [I] hdc : device context handle
3947  * [I] cdmode : custom draw mode
3948  *
3949  * RETURN:
3950  * None
3951  */
3952 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3953 {
3954     POINT Origin, Position;
3955
3956     /* Get scroll info once before loop */
3957     LISTVIEW_GetOrigin(infoPtr, &Origin);
3958     
3959     while(iterator_prev(i))
3960     {
3961         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3962         Position.x += Origin.x;
3963         Position.y += Origin.y;
3964
3965         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3966     }
3967 }
3968
3969
3970 /***
3971  * DESCRIPTION:
3972  * Draws listview items.
3973  *
3974  * PARAMETER(S):
3975  * [I] infoPtr : valid pointer to the listview structure
3976  * [I] hdc : device context handle
3977  *
3978  * RETURN:
3979  * NoneX
3980  */
3981 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3982 {
3983     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3984     COLORREF oldTextColor, oldClrTextBk, oldClrText;
3985     NMLVCUSTOMDRAW nmlvcd;
3986     HFONT hOldFont;
3987     DWORD cdmode;
3988     INT oldBkMode;
3989     RECT rcClient;
3990     ITERATOR i;
3991
3992     LISTVIEW_DUMP(infoPtr);
3993   
3994     infoPtr->bIsDrawing = TRUE;
3995
3996     /* save dc values we're gonna trash while drawing */
3997     hOldFont = SelectObject(hdc, infoPtr->hFont);
3998     oldBkMode = GetBkMode(hdc);
3999     infoPtr->clrTextBkDefault = GetBkColor(hdc);
4000     oldTextColor = GetTextColor(hdc);
4001
4002     oldClrTextBk = infoPtr->clrTextBk;
4003     oldClrText   = infoPtr->clrText;
4004    
4005     infoPtr->cditemmode = CDRF_DODEFAULT;
4006
4007     GetClientRect(infoPtr->hwndSelf, &rcClient);
4008     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4009     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4010     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4011     prepaint_setup(infoPtr, hdc, &nmlvcd);
4012
4013     /* Use these colors to draw the items */
4014     infoPtr->clrTextBk = nmlvcd.clrTextBk;
4015     infoPtr->clrText = nmlvcd.clrText;
4016
4017     /* nothing to draw */
4018     if(infoPtr->nItemCount == 0) goto enddraw;
4019
4020     /* figure out what we need to draw */
4021     iterator_visibleitems(&i, infoPtr, hdc);
4022     
4023     /* send cache hint notification */
4024     if (infoPtr->dwStyle & LVS_OWNERDATA)
4025     {
4026         RANGE range = iterator_range(&i);
4027         NMLVCACHEHINT nmlv;
4028         
4029         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4030         nmlv.iFrom = range.lower;
4031         nmlv.iTo   = range.upper - 1;
4032         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4033     }
4034
4035     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4036         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4037     else
4038     {
4039         if (uView == LVS_REPORT)
4040             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4041         else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4042             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4043
4044         /* if we have a focus rect, draw it */
4045         if (infoPtr->bFocus)
4046             DrawFocusRect(hdc, &infoPtr->rcFocus);
4047     }
4048     iterator_destroy(&i);
4049     
4050 enddraw:
4051     if (cdmode & CDRF_NOTIFYPOSTPAINT)
4052         notify_postpaint(infoPtr, &nmlvcd);
4053
4054     infoPtr->clrTextBk = oldClrTextBk;
4055     infoPtr->clrText = oldClrText;
4056
4057     SelectObject(hdc, hOldFont);
4058     SetBkMode(hdc, oldBkMode);
4059     SetBkColor(hdc, infoPtr->clrTextBkDefault);
4060     SetTextColor(hdc, oldTextColor);
4061     infoPtr->bIsDrawing = FALSE;
4062 }
4063
4064
4065 /***
4066  * DESCRIPTION:
4067  * Calculates the approximate width and height of a given number of items.
4068  *
4069  * PARAMETER(S):
4070  * [I] infoPtr : valid pointer to the listview structure
4071  * [I] nItemCount : number of items
4072  * [I] wWidth : width
4073  * [I] wHeight : height
4074  *
4075  * RETURN:
4076  * Returns a DWORD. The width in the low word and the height in high word.
4077  */
4078 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4079                                             WORD wWidth, WORD wHeight)
4080 {
4081   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4082   INT nItemCountPerColumn = 1;
4083   INT nColumnCount = 0;
4084   DWORD dwViewRect = 0;
4085
4086   if (nItemCount == -1)
4087     nItemCount = infoPtr->nItemCount;
4088
4089   if (uView == LVS_LIST)
4090   {
4091     if (wHeight == 0xFFFF)
4092     {
4093       /* use current height */
4094       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4095     }
4096
4097     if (wHeight < infoPtr->nItemHeight)
4098       wHeight = infoPtr->nItemHeight;
4099
4100     if (nItemCount > 0)
4101     {
4102       if (infoPtr->nItemHeight > 0)
4103       {
4104         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4105         if (nItemCountPerColumn == 0)
4106           nItemCountPerColumn = 1;
4107
4108         if (nItemCount % nItemCountPerColumn != 0)
4109           nColumnCount = nItemCount / nItemCountPerColumn;
4110         else
4111           nColumnCount = nItemCount / nItemCountPerColumn + 1;
4112       }
4113     }
4114
4115     /* Microsoft padding magic */
4116     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4117     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4118
4119     dwViewRect = MAKELONG(wWidth, wHeight);
4120   }
4121   else if (uView == LVS_REPORT)
4122   {
4123     RECT rcBox;
4124
4125     if (infoPtr->nItemCount > 0)
4126     {
4127       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4128       wWidth = rcBox.right - rcBox.left;
4129       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4130     }
4131     else
4132     {
4133       /* use current height and width */
4134       if (wHeight == 0xffff)
4135           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4136       if (wWidth == 0xffff)
4137           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4138     }
4139
4140     dwViewRect = MAKELONG(wWidth, wHeight);
4141   }
4142   else if (uView == LVS_SMALLICON)
4143     FIXME("uView == LVS_SMALLICON: not implemented\n");
4144   else if (uView == LVS_ICON)
4145     FIXME("uView == LVS_ICON: not implemented\n");
4146
4147   return dwViewRect;
4148 }
4149
4150
4151 /***
4152  * DESCRIPTION:
4153  * Create a drag image list for the specified item.
4154  *
4155  * PARAMETER(S):
4156  * [I] infoPtr : valid pointer to the listview structure
4157  * [I] iItem   : index of item
4158  * [O] lppt    : Upperr-left corner of the image
4159  *
4160  * RETURN:
4161  * Returns a handle to the image list if successful, NULL otherwise.
4162  */
4163 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4164 {
4165     RECT rcItem;
4166     SIZE size;
4167     POINT pos;
4168     HDC hdc, hdcOrig;
4169     HBITMAP hbmp, hOldbmp;
4170     HIMAGELIST dragList = 0;
4171     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4172
4173     if (iItem < 0 || iItem >= infoPtr->nItemCount)
4174         return 0;
4175
4176     rcItem.left = LVIR_BOUNDS;
4177     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4178         return 0;
4179
4180     lppt->x = rcItem.left;
4181     lppt->y = rcItem.top;
4182
4183     size.cx = rcItem.right - rcItem.left;
4184     size.cy = rcItem.bottom - rcItem.top;
4185
4186     hdcOrig = GetDC(infoPtr->hwndSelf);
4187     hdc = CreateCompatibleDC(hdcOrig);
4188     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4189     hOldbmp = SelectObject(hdc, hbmp);
4190
4191     rcItem.left = rcItem.top = 0;
4192     rcItem.right = size.cx;
4193     rcItem.bottom = size.cy;
4194     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4195     
4196     pos.x = pos.y = 0;
4197     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4198     {
4199         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4200         SelectObject(hdc, hOldbmp);
4201         ImageList_Add(dragList, hbmp, 0);
4202     }
4203     else
4204         SelectObject(hdc, hOldbmp);
4205
4206     DeleteObject(hbmp);
4207     DeleteDC(hdc);
4208     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4209
4210     TRACE("ret=%p\n", dragList);
4211
4212     return dragList;
4213 }
4214
4215
4216 /***
4217  * DESCRIPTION:
4218  * Removes all listview items and subitems.
4219  *
4220  * PARAMETER(S):
4221  * [I] infoPtr : valid pointer to the listview structure
4222  *
4223  * RETURN:
4224  *   SUCCESS : TRUE
4225  *   FAILURE : FALSE
4226  */
4227 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4228 {
4229     NMLISTVIEW nmlv;
4230     HDPA hdpaSubItems = NULL;
4231     BOOL bSuppress;
4232     ITEMHDR *hdrItem;
4233     INT i, j;
4234
4235     TRACE("()\n");
4236
4237     /* we do it directly, to avoid notifications */
4238     ranges_clear(infoPtr->selectionRanges);
4239     infoPtr->nSelectionMark = -1;
4240     infoPtr->nFocusedItem = -1;
4241     SetRectEmpty(&infoPtr->rcFocus);
4242     /* But we are supposed to leave nHotItem as is! */
4243
4244
4245     /* send LVN_DELETEALLITEMS notification */
4246     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4247     nmlv.iItem = -1;
4248     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4249
4250     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4251     {
4252         /* send LVN_DELETEITEM notification, if not suppressed */
4253         if (!bSuppress) notify_deleteitem(infoPtr, i);
4254         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4255         {
4256             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4257             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4258             {
4259                 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4260                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4261                 Free(hdrItem);
4262             }
4263             DPA_Destroy(hdpaSubItems);
4264             DPA_DeletePtr(infoPtr->hdpaItems, i);
4265         }
4266         DPA_DeletePtr(infoPtr->hdpaPosX, i);
4267         DPA_DeletePtr(infoPtr->hdpaPosY, i);
4268         infoPtr->nItemCount --;
4269     }
4270     
4271     LISTVIEW_UpdateScroll(infoPtr);
4272
4273     LISTVIEW_InvalidateList(infoPtr);
4274     
4275     return TRUE;
4276 }
4277
4278 /***
4279  * DESCRIPTION:
4280  * Scrolls, and updates the columns, when a column is changing width.
4281  *
4282  * PARAMETER(S):
4283  * [I] infoPtr : valid pointer to the listview structure
4284  * [I] nColumn : column to scroll
4285  * [I] dx : amount of scroll, in pixels
4286  *
4287  * RETURN:
4288  *   None.
4289  */
4290 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4291 {
4292     COLUMN_INFO *lpColumnInfo;
4293     RECT rcOld, rcCol;
4294     POINT ptOrigin;
4295     INT nCol;
4296    
4297     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4298     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4299     rcCol = lpColumnInfo->rcHeader;
4300     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4301         rcCol.left = rcCol.right;
4302     
4303     /* ajust the other columns */
4304     for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4305     {
4306         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4307         lpColumnInfo->rcHeader.left += dx;
4308         lpColumnInfo->rcHeader.right += dx;
4309     }
4310
4311     /* do not update screen if not in report mode */
4312     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4313     
4314     /* if we have a focus, must first erase the focus rect */
4315     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4316     
4317     /* Need to reset the item width when inserting a new column */
4318     infoPtr->nItemWidth += dx;
4319
4320     LISTVIEW_UpdateScroll(infoPtr);
4321     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4322
4323     /* scroll to cover the deleted column, and invalidate for redraw */
4324     rcOld = infoPtr->rcList;
4325     rcOld.left = ptOrigin.x + rcCol.left + dx;
4326     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4327     
4328     /* we can restore focus now */
4329     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4330 }
4331
4332 /***
4333  * DESCRIPTION:
4334  * Removes a column from the listview control.
4335  *
4336  * PARAMETER(S):
4337  * [I] infoPtr : valid pointer to the listview structure
4338  * [I] nColumn : column index
4339  *
4340  * RETURN:
4341  *   SUCCESS : TRUE
4342  *   FAILURE : FALSE
4343  */
4344 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4345 {
4346     RECT rcCol;
4347     
4348     TRACE("nColumn=%d\n", nColumn);
4349
4350     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4351            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4352
4353     /* While the MSDN specifically says that column zero should not be deleted,
4354        what actually happens is that the column itself is deleted but no items or subitems
4355        are removed.
4356      */
4357
4358     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4359     
4360     if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4361         return FALSE;
4362
4363     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4364     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4365   
4366     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4367     {
4368         SUBITEM_INFO *lpSubItem, *lpDelItem;
4369         HDPA hdpaSubItems;
4370         INT nItem, nSubItem, i;
4371         
4372         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4373         {
4374             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4375             nSubItem = 0;
4376             lpDelItem = 0;
4377             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4378             {
4379                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4380                 if (lpSubItem->iSubItem == nColumn)
4381                 {
4382                     nSubItem = i;
4383                     lpDelItem = lpSubItem;
4384                 }
4385                 else if (lpSubItem->iSubItem > nColumn) 
4386                 {
4387                     lpSubItem->iSubItem--;
4388                 }
4389             }
4390
4391             /* if we found our subitem, zapp it */      
4392             if (nSubItem > 0)
4393             {
4394                 /* free string */
4395                 if (is_textW(lpDelItem->hdr.pszText))
4396                     Free(lpDelItem->hdr.pszText);
4397
4398                 /* free item */
4399                 Free(lpDelItem);
4400
4401                 /* free dpa memory */
4402                 DPA_DeletePtr(hdpaSubItems, nSubItem);
4403             }
4404         }
4405     }
4406
4407     /* update the other column info */
4408     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4409         LISTVIEW_InvalidateList(infoPtr);
4410     else
4411         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4412
4413     return TRUE;
4414 }
4415
4416 /***
4417  * DESCRIPTION:
4418  * Invalidates the listview after an item's insertion or deletion.
4419  *
4420  * PARAMETER(S):
4421  * [I] infoPtr : valid pointer to the listview structure
4422  * [I] nItem : item index
4423  * [I] dir : -1 if deleting, 1 if inserting
4424  *
4425  * RETURN:
4426  *   None
4427  */
4428 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4429 {
4430     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4431     INT nPerCol, nItemCol, nItemRow;
4432     RECT rcScroll;
4433     POINT Origin;
4434
4435     /* if we don't refresh, what's the point of scrolling? */
4436     if (!is_redrawing(infoPtr)) return;
4437     
4438     assert (abs(dir) == 1);
4439
4440     /* arrange icons if autoarrange is on */
4441     if (is_autoarrange(infoPtr))
4442     {
4443         BOOL arrange = TRUE;
4444         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4445         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4446         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4447     }
4448
4449     /* scrollbars need updating */
4450     LISTVIEW_UpdateScroll(infoPtr);
4451
4452     /* figure out the item's position */ 
4453     if (uView == LVS_REPORT)
4454         nPerCol = infoPtr->nItemCount + 1;
4455     else if (uView == LVS_LIST)
4456         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4457     else /* LVS_ICON, or LVS_SMALLICON */
4458         return;
4459     
4460     nItemCol = nItem / nPerCol;
4461     nItemRow = nItem % nPerCol;
4462     LISTVIEW_GetOrigin(infoPtr, &Origin);
4463
4464     /* move the items below up a slot */
4465     rcScroll.left = nItemCol * infoPtr->nItemWidth;
4466     rcScroll.top = nItemRow * infoPtr->nItemHeight;
4467     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4468     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4469     OffsetRect(&rcScroll, Origin.x, Origin.y);
4470     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4471     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4472     {
4473         TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4474         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
4475                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4476     }
4477
4478     /* report has only that column, so we're done */
4479     if (uView == LVS_REPORT) return;
4480
4481     /* now for LISTs, we have to deal with the columns to the right */
4482     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4483     rcScroll.top = 0;
4484     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4485     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4486     OffsetRect(&rcScroll, Origin.x, Origin.y);
4487     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4488         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4489                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4490 }
4491
4492 /***
4493  * DESCRIPTION:
4494  * Removes an item from the listview control.
4495  *
4496  * PARAMETER(S):
4497  * [I] infoPtr : valid pointer to the listview structure
4498  * [I] nItem : item index
4499  *
4500  * RETURN:
4501  *   SUCCESS : TRUE
4502  *   FAILURE : FALSE
4503  */
4504 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4505 {
4506     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4507     LVITEMW item;
4508
4509     TRACE("(nItem=%d)\n", nItem);
4510
4511     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4512     
4513     /* remove selection, and focus */
4514     item.state = 0;
4515     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4516     LISTVIEW_SetItemState(infoPtr, nItem, &item);
4517             
4518     /* send LVN_DELETEITEM notification. */
4519     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4520
4521     /* we need to do this here, because we'll be deleting stuff */  
4522     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4523         LISTVIEW_InvalidateItem(infoPtr, nItem);
4524     
4525     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4526     {
4527         HDPA hdpaSubItems;
4528         ITEMHDR *hdrItem;
4529         INT i;
4530
4531         hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);  
4532         for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4533         {
4534             hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4535             if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4536             Free(hdrItem);
4537         }
4538         DPA_Destroy(hdpaSubItems);
4539     }
4540
4541     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4542     {
4543         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4544         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4545     }
4546
4547     infoPtr->nItemCount--;
4548     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4549
4550     /* now is the invalidation fun */
4551     LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4552     return TRUE;
4553 }
4554
4555
4556 /***
4557  * DESCRIPTION:
4558  * Callback implementation for editlabel control
4559  *
4560  * PARAMETER(S):
4561  * [I] infoPtr : valid pointer to the listview structure
4562  * [I] pszText : modified text
4563  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4564  *
4565  * RETURN:
4566  *   SUCCESS : TRUE
4567  *   FAILURE : FALSE
4568  */
4569 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4570 {
4571     HWND hwndSelf = infoPtr->hwndSelf;
4572     NMLVDISPINFOW dispInfo;
4573
4574     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4575
4576     ZeroMemory(&dispInfo, sizeof(dispInfo));
4577     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4578     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4579     dispInfo.item.iSubItem = 0;
4580     dispInfo.item.stateMask = ~0;
4581     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4582     /* add the text from the edit in */
4583     dispInfo.item.mask |= LVIF_TEXT;
4584     dispInfo.item.pszText = pszText;
4585     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4586
4587     /* Do we need to update the Item Text */
4588     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4589     if (!IsWindow(hwndSelf))
4590         return FALSE;
4591     if (!pszText) return TRUE;
4592
4593     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4594     {
4595         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4596         ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4597         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4598         {
4599             LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4600             return TRUE;
4601         }
4602     }
4603
4604     ZeroMemory(&dispInfo, sizeof(dispInfo));
4605     dispInfo.item.mask = LVIF_TEXT;
4606     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4607     dispInfo.item.iSubItem = 0;
4608     dispInfo.item.pszText = pszText;
4609     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4610     return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4611 }
4612
4613 /***
4614  * DESCRIPTION:
4615  * Begin in place editing of specified list view item
4616  *
4617  * PARAMETER(S):
4618  * [I] infoPtr : valid pointer to the listview structure
4619  * [I] nItem : item index
4620  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4621  *
4622  * RETURN:
4623  *   SUCCESS : TRUE
4624  *   FAILURE : FALSE
4625  */
4626 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4627 {
4628     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4629     NMLVDISPINFOW dispInfo;
4630     RECT rect;
4631     HWND hwndSelf = infoPtr->hwndSelf;
4632
4633     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4634
4635     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4636     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4637
4638     infoPtr->nEditLabelItem = nItem;
4639
4640     /* Is the EditBox still there, if so remove it */
4641     if(infoPtr->hwndEdit != 0)
4642     {
4643         SetFocus(infoPtr->hwndSelf);
4644         infoPtr->hwndEdit = 0;
4645     }
4646
4647     LISTVIEW_SetSelection(infoPtr, nItem);
4648     LISTVIEW_SetItemFocus(infoPtr, nItem);
4649     LISTVIEW_InvalidateItem(infoPtr, nItem);
4650
4651     rect.left = LVIR_LABEL;
4652     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4653     
4654     ZeroMemory(&dispInfo, sizeof(dispInfo));
4655     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4656     dispInfo.item.iItem = nItem;
4657     dispInfo.item.iSubItem = 0;
4658     dispInfo.item.stateMask = ~0;
4659     dispInfo.item.pszText = szDispText;
4660     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4661     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4662
4663     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4664                     rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4665     if (!infoPtr->hwndEdit) return 0;
4666     
4667     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4668     {
4669         if (!IsWindow(hwndSelf))
4670             return 0;
4671         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4672         infoPtr->hwndEdit = 0;
4673         return 0;
4674     }
4675
4676     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4677     SetFocus(infoPtr->hwndEdit);
4678     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4679     return infoPtr->hwndEdit;
4680 }
4681
4682
4683 /***
4684  * DESCRIPTION:
4685  * Ensures the specified item is visible, scrolling into view if necessary.
4686  *
4687  * PARAMETER(S):
4688  * [I] infoPtr : valid pointer to the listview structure
4689  * [I] nItem : item index
4690  * [I] bPartial : partially or entirely visible
4691  *
4692  * RETURN:
4693  *   SUCCESS : TRUE
4694  *   FAILURE : FALSE
4695  */
4696 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4697 {
4698     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4699     INT nScrollPosHeight = 0;
4700     INT nScrollPosWidth = 0;
4701     INT nHorzAdjust = 0;
4702     INT nVertAdjust = 0;
4703     INT nHorzDiff = 0;
4704     INT nVertDiff = 0;
4705     RECT rcItem, rcTemp;
4706
4707     rcItem.left = LVIR_BOUNDS;
4708     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4709
4710     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4711     
4712     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4713     {
4714         /* scroll left/right, but in LVS_REPORT mode */
4715         if (uView == LVS_LIST)
4716             nScrollPosWidth = infoPtr->nItemWidth;
4717         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4718             nScrollPosWidth = 1;
4719
4720         if (rcItem.left < infoPtr->rcList.left)
4721         {
4722             nHorzAdjust = -1;
4723             if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4724         }
4725         else
4726         {
4727             nHorzAdjust = 1;
4728             if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4729         }
4730     }
4731
4732     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4733     {
4734         /* scroll up/down, but not in LVS_LIST mode */
4735         if (uView == LVS_REPORT)
4736             nScrollPosHeight = infoPtr->nItemHeight;
4737         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4738             nScrollPosHeight = 1;
4739
4740         if (rcItem.top < infoPtr->rcList.top)
4741         {
4742             nVertAdjust = -1;
4743             if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4744         }
4745         else
4746         {
4747             nVertAdjust = 1;
4748             if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4749         }
4750     }
4751
4752     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4753
4754     if (nScrollPosWidth)
4755     {
4756         INT diff = nHorzDiff / nScrollPosWidth;
4757         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4758         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4759     }
4760
4761     if (nScrollPosHeight)
4762     {
4763         INT diff = nVertDiff / nScrollPosHeight;
4764         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4765         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4766     }
4767
4768     return TRUE;
4769 }
4770
4771 /***
4772  * DESCRIPTION:
4773  * Searches for an item with specific characteristics.
4774  *
4775  * PARAMETER(S):
4776  * [I] hwnd : window handle
4777  * [I] nStart : base item index
4778  * [I] lpFindInfo : item information to look for
4779  *
4780  * RETURN:
4781  *   SUCCESS : index of item
4782  *   FAILURE : -1
4783  */
4784 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4785                               const LVFINDINFOW *lpFindInfo)
4786 {
4787     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4788     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4789     BOOL bWrap = FALSE, bNearest = FALSE;
4790     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4791     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4792     POINT Position, Destination;
4793     LVITEMW lvItem;
4794
4795     if (!lpFindInfo || nItem < 0) return -1;
4796     
4797     lvItem.mask = 0;
4798     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4799     {
4800         lvItem.mask |= LVIF_TEXT;
4801         lvItem.pszText = szDispText;
4802         lvItem.cchTextMax = DISP_TEXT_SIZE;
4803     }
4804
4805     if (lpFindInfo->flags & LVFI_WRAP)
4806         bWrap = TRUE;
4807
4808     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
4809         (uView == LVS_ICON || uView ==LVS_SMALLICON))
4810     {
4811         POINT Origin;
4812         RECT rcArea;
4813         
4814         LISTVIEW_GetOrigin(infoPtr, &Origin);
4815         Destination.x = lpFindInfo->pt.x - Origin.x;
4816         Destination.y = lpFindInfo->pt.y - Origin.y;
4817         switch(lpFindInfo->vkDirection)
4818         {
4819         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
4820         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
4821         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4822         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
4823         case VK_HOME:  Destination.x = Destination.y = 0; break;
4824         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4825         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4826         case VK_END:
4827             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4828             Destination.x = rcArea.right; 
4829             Destination.y = rcArea.bottom; 
4830             break;
4831         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4832         }
4833         bNearest = TRUE;
4834     }
4835     else Destination.x = Destination.y = 0;
4836
4837     /* if LVFI_PARAM is specified, all other flags are ignored */
4838     if (lpFindInfo->flags & LVFI_PARAM)
4839     {
4840         lvItem.mask |= LVIF_PARAM;
4841         bNearest = FALSE;
4842         lvItem.mask &= ~LVIF_TEXT;
4843     }
4844
4845 again:
4846     for (; nItem < nLast; nItem++)
4847     {
4848         lvItem.iItem = nItem;
4849         lvItem.iSubItem = 0;
4850         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4851
4852         if (lvItem.mask & LVIF_PARAM)
4853         {
4854             if (lpFindInfo->lParam == lvItem.lParam)
4855                 return nItem;
4856             else
4857                 continue;
4858         }
4859         
4860         if (lvItem.mask & LVIF_TEXT)
4861         {
4862             if (lpFindInfo->flags & LVFI_PARTIAL)
4863             {
4864                 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4865             }
4866             else
4867             {
4868                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4869             }
4870         }
4871
4872         if (!bNearest) return nItem;
4873         
4874         /* This is very inefficient. To do a good job here,
4875          * we need a sorted array of (x,y) item positions */
4876         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4877
4878         /* compute the distance^2 to the destination */
4879         xdist = Destination.x - Position.x;
4880         ydist = Destination.y - Position.y;
4881         dist = xdist * xdist + ydist * ydist;
4882
4883         /* remember the distance, and item if it's closer */
4884         if (dist < mindist)
4885         {
4886             mindist = dist;
4887             nNearestItem = nItem;
4888         }
4889     }
4890
4891     if (bWrap)
4892     {
4893         nItem = 0;
4894         nLast = min(nStart + 1, infoPtr->nItemCount);
4895         bWrap = FALSE;
4896         goto again;
4897     }
4898
4899     return nNearestItem;
4900 }
4901
4902 /***
4903  * DESCRIPTION:
4904  * Searches for an item with specific characteristics.
4905  *
4906  * PARAMETER(S):
4907  * [I] hwnd : window handle
4908  * [I] nStart : base item index
4909  * [I] lpFindInfo : item information to look for
4910  *
4911  * RETURN:
4912  *   SUCCESS : index of item
4913  *   FAILURE : -1
4914  */
4915 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4916                               const LVFINDINFOA *lpFindInfo)
4917 {
4918     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4919     LVFINDINFOW fiw;
4920     INT res;
4921
4922     memcpy(&fiw, lpFindInfo, sizeof(fiw));
4923     if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4924     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4925     if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4926     return res;
4927 }
4928
4929 /***
4930  * DESCRIPTION:
4931  * Retrieves the background image of the listview control.
4932  *
4933  * PARAMETER(S):
4934  * [I] infoPtr : valid pointer to the listview structure
4935  * [O] lpBkImage : background image attributes
4936  *
4937  * RETURN:
4938  *   SUCCESS : TRUE
4939  *   FAILURE : FALSE
4940  */
4941 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
4942 /* {   */
4943 /*   FIXME (listview, "empty stub!\n"); */
4944 /*   return FALSE;   */
4945 /* }   */
4946
4947 /***
4948  * DESCRIPTION:
4949  * Retrieves column attributes.
4950  *
4951  * PARAMETER(S):
4952  * [I] infoPtr : valid pointer to the listview structure
4953  * [I] nColumn :  column index
4954  * [IO] lpColumn : column information
4955  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4956  *           otherwise it is in fact a LPLVCOLUMNA
4957  *
4958  * RETURN:
4959  *   SUCCESS : TRUE
4960  *   FAILURE : FALSE
4961  */
4962 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4963 {
4964     COLUMN_INFO *lpColumnInfo;
4965     HDITEMW hdi;
4966
4967     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4968     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4969
4970     /* initialize memory */
4971     ZeroMemory(&hdi, sizeof(hdi));
4972
4973     if (lpColumn->mask & LVCF_TEXT)
4974     {
4975         hdi.mask |= HDI_TEXT;
4976         hdi.pszText = lpColumn->pszText;
4977         hdi.cchTextMax = lpColumn->cchTextMax;
4978     }
4979
4980     if (lpColumn->mask & LVCF_IMAGE)
4981         hdi.mask |= HDI_IMAGE;
4982
4983     if (lpColumn->mask & LVCF_ORDER)
4984         hdi.mask |= HDI_ORDER;
4985
4986     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4987
4988     if (lpColumn->mask & LVCF_FMT)
4989         lpColumn->fmt = lpColumnInfo->fmt;
4990
4991     if (lpColumn->mask & LVCF_WIDTH)
4992         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4993
4994     if (lpColumn->mask & LVCF_IMAGE)
4995         lpColumn->iImage = hdi.iImage;
4996
4997     if (lpColumn->mask & LVCF_ORDER)
4998         lpColumn->iOrder = hdi.iOrder;
4999
5000     return TRUE;
5001 }
5002
5003
5004 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5005 {
5006     INT i;
5007
5008     if (!lpiArray)
5009         return FALSE;
5010
5011     /* FIXME: little hack */
5012     for (i = 0; i < iCount; i++)
5013         lpiArray[i] = i;
5014
5015     return TRUE;
5016 }
5017
5018 /***
5019  * DESCRIPTION:
5020  * Retrieves the column width.
5021  *
5022  * PARAMETER(S):
5023  * [I] infoPtr : valid pointer to the listview structure
5024  * [I] int : column index
5025  *
5026  * RETURN:
5027  *   SUCCESS : column width
5028  *   FAILURE : zero
5029  */
5030 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5031 {
5032     INT nColumnWidth = 0;
5033     RECT rcHeader;
5034
5035     TRACE("nColumn=%d\n", nColumn);
5036
5037     /* we have a 'column' in LIST and REPORT mode only */
5038     switch(infoPtr->dwStyle & LVS_TYPEMASK)
5039     {
5040     case LVS_LIST:
5041         nColumnWidth = infoPtr->nItemWidth;
5042         break;
5043     case LVS_REPORT:
5044         if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5045         LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5046         nColumnWidth = rcHeader.right - rcHeader.left;
5047         break;
5048     }
5049
5050     TRACE("nColumnWidth=%d\n", nColumnWidth);
5051     return nColumnWidth;
5052 }
5053
5054 /***
5055  * DESCRIPTION:
5056  * In list or report display mode, retrieves the number of items that can fit
5057  * vertically in the visible area. In icon or small icon display mode,
5058  * retrieves the total number of visible items.
5059  *
5060  * PARAMETER(S):
5061  * [I] infoPtr : valid pointer to the listview structure
5062  *
5063  * RETURN:
5064  * Number of fully visible items.
5065  */
5066 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5067 {
5068     switch (infoPtr->dwStyle & LVS_TYPEMASK)
5069     {
5070     case LVS_ICON:
5071     case LVS_SMALLICON:
5072         return infoPtr->nItemCount;
5073     case LVS_REPORT:
5074         return LISTVIEW_GetCountPerColumn(infoPtr);
5075     case LVS_LIST:
5076         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5077     }
5078     assert(FALSE);
5079     return 0;
5080 }
5081
5082 /***
5083  * DESCRIPTION:
5084  * Retrieves an image list handle.
5085  *
5086  * PARAMETER(S):
5087  * [I] infoPtr : valid pointer to the listview structure
5088  * [I] nImageList : image list identifier
5089  *
5090  * RETURN:
5091  *   SUCCESS : image list handle
5092  *   FAILURE : NULL
5093  */
5094 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5095 {
5096     switch (nImageList)
5097     {
5098     case LVSIL_NORMAL: return infoPtr->himlNormal;
5099     case LVSIL_SMALL: return infoPtr->himlSmall;
5100     case LVSIL_STATE: return infoPtr->himlState;
5101     }
5102     return NULL;
5103 }
5104
5105 /* LISTVIEW_GetISearchString */
5106
5107 /***
5108  * DESCRIPTION:
5109  * Retrieves item attributes.
5110  *
5111  * PARAMETER(S):
5112  * [I] hwnd : window handle
5113  * [IO] lpLVItem : item info
5114  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5115  *           if FALSE, the lpLVItem is a LPLVITEMA.
5116  *
5117  * NOTE:
5118  *   This is the internal 'GetItem' interface -- it tries to
5119  *   be smart and avoid text copies, if possible, by modifying
5120  *   lpLVItem->pszText to point to the text string. Please note
5121  *   that this is not always possible (e.g. OWNERDATA), so on
5122  *   entry you *must* supply valid values for pszText, and cchTextMax.
5123  *   The only difference to the documented interface is that upon
5124  *   return, you should use *only* the lpLVItem->pszText, rather than
5125  *   the buffer pointer you provided on input. Most code already does
5126  *   that, so it's not a problem.
5127  *   For the two cases when the text must be copied (that is,
5128  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5129  *
5130  * RETURN:
5131  *   SUCCESS : TRUE
5132  *   FAILURE : FALSE
5133  */
5134 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5135 {
5136     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5137     NMLVDISPINFOW dispInfo;
5138     ITEM_INFO *lpItem;
5139     ITEMHDR* pItemHdr;
5140     HDPA hdpaSubItems;
5141     INT isubitem;
5142
5143     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5144
5145     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5146         return FALSE;
5147
5148     if (lpLVItem->mask == 0) return TRUE;
5149
5150     /* make a local copy */
5151     isubitem = lpLVItem->iSubItem;
5152
5153     /* a quick optimization if all we're asked is the focus state
5154      * these queries are worth optimising since they are common,
5155      * and can be answered in constant time, without the heavy accesses */
5156     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5157          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5158     {
5159         lpLVItem->state = 0;
5160         if (infoPtr->nFocusedItem == lpLVItem->iItem)
5161             lpLVItem->state |= LVIS_FOCUSED;
5162         return TRUE;
5163     }
5164
5165     ZeroMemory(&dispInfo, sizeof(dispInfo));
5166
5167     /* if the app stores all the data, handle it separately */
5168     if (infoPtr->dwStyle & LVS_OWNERDATA)
5169     {
5170         dispInfo.item.state = 0;
5171
5172         /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5173         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5174         {
5175             /* NOTE: copy only fields which we _know_ are initialized, some apps
5176              *       depend on the uninitialized fields being 0 */
5177             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5178             dispInfo.item.iItem = lpLVItem->iItem;
5179             dispInfo.item.iSubItem = isubitem;
5180             if (lpLVItem->mask & LVIF_TEXT)
5181             {
5182                 dispInfo.item.pszText = lpLVItem->pszText;
5183                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;                
5184             }
5185             if (lpLVItem->mask & LVIF_STATE)
5186                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5187             notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5188             dispInfo.item.stateMask = lpLVItem->stateMask;
5189             if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5190             {
5191                 /* full size structure expected - _WIN32IE >= 0x560 */
5192                 *lpLVItem = dispInfo.item;
5193             }
5194             else if (lpLVItem->mask & LVIF_INDENT)
5195             {
5196                 /* indent member expected - _WIN32IE >= 0x300 */
5197                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5198             }
5199             else
5200             {
5201                 /* minimal structure expected */
5202                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5203             }
5204             TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5205         }
5206         
5207         /* make sure lParam is zeroed out */
5208         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5209         
5210         /* we store only a little state, so if we're not asked, we're done */
5211         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5212
5213         /* if focus is handled by us, report it */
5214         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5215         {
5216             lpLVItem->state &= ~LVIS_FOCUSED;
5217             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5218                 lpLVItem->state |= LVIS_FOCUSED;
5219         }
5220
5221         /* and do the same for selection, if we handle it */
5222         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5223         {
5224             lpLVItem->state &= ~LVIS_SELECTED;
5225             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5226                 lpLVItem->state |= LVIS_SELECTED;
5227         }
5228         
5229         return TRUE;
5230     }
5231
5232     /* find the item and subitem structures before we proceed */
5233     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5234     lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5235     assert (lpItem);
5236
5237     if (isubitem)
5238     {
5239         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5240         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5241         if (!lpSubItem)
5242         {
5243             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5244             isubitem = 0;
5245         }
5246     }
5247     else
5248         pItemHdr = &lpItem->hdr;
5249
5250     /* Do we need to query the state from the app? */
5251     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5252     {
5253         dispInfo.item.mask |= LVIF_STATE;
5254         dispInfo.item.stateMask = infoPtr->uCallbackMask;
5255     }
5256   
5257     /* Do we need to enquire about the image? */
5258     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5259         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5260     {
5261         dispInfo.item.mask |= LVIF_IMAGE;
5262         dispInfo.item.iImage = I_IMAGECALLBACK;
5263     }
5264
5265     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5266     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5267     {
5268         dispInfo.item.mask |= LVIF_TEXT;
5269         dispInfo.item.pszText = lpLVItem->pszText;
5270         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5271         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5272             *dispInfo.item.pszText = '\0';
5273     }
5274
5275     /* If we don't have all the requested info, query the application */
5276     if (dispInfo.item.mask != 0)
5277     {
5278         dispInfo.item.iItem = lpLVItem->iItem;
5279         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5280         dispInfo.item.lParam = lpItem->lParam;
5281         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5282         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5283     }
5284
5285     /* we should not store values for subitems */
5286     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5287
5288     /* Now, handle the iImage field */
5289     if (dispInfo.item.mask & LVIF_IMAGE)
5290     {
5291         lpLVItem->iImage = dispInfo.item.iImage;
5292         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5293             pItemHdr->iImage = dispInfo.item.iImage;
5294     }
5295     else if (lpLVItem->mask & LVIF_IMAGE)
5296     {
5297         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5298             lpLVItem->iImage = pItemHdr->iImage;
5299         else
5300             lpLVItem->iImage = 0;
5301     }
5302
5303     /* The pszText field */
5304     if (dispInfo.item.mask & LVIF_TEXT)
5305     {
5306         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5307             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5308
5309         lpLVItem->pszText = dispInfo.item.pszText;
5310     }
5311     else if (lpLVItem->mask & LVIF_TEXT)
5312     {
5313         if (isW) lpLVItem->pszText = pItemHdr->pszText;
5314         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5315     }
5316
5317     /* if this is a subitem, we're done */
5318     if (isubitem) return TRUE;
5319   
5320     /* Next is the lParam field */
5321     if (dispInfo.item.mask & LVIF_PARAM)
5322     {
5323         lpLVItem->lParam = dispInfo.item.lParam;
5324         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5325             lpItem->lParam = dispInfo.item.lParam;
5326     }
5327     else if (lpLVItem->mask & LVIF_PARAM)
5328         lpLVItem->lParam = lpItem->lParam;
5329
5330     /* ... the state field (this one is different due to uCallbackmask) */
5331     if (lpLVItem->mask & LVIF_STATE) 
5332     {
5333         lpLVItem->state = lpItem->state;
5334         if (dispInfo.item.mask & LVIF_STATE)
5335         {
5336             lpLVItem->state &= ~dispInfo.item.stateMask;
5337             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5338         }
5339         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5340         {
5341             lpLVItem->state &= ~LVIS_FOCUSED;
5342             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5343                 lpLVItem->state |= LVIS_FOCUSED;
5344         }
5345         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5346         {
5347             lpLVItem->state &= ~LVIS_SELECTED;
5348             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5349                 lpLVItem->state |= LVIS_SELECTED;
5350         }           
5351     }
5352
5353     /* and last, but not least, the indent field */
5354     if (lpLVItem->mask & LVIF_INDENT)
5355         lpLVItem->iIndent = lpItem->iIndent;
5356
5357     return TRUE;
5358 }
5359
5360 /***
5361  * DESCRIPTION:
5362  * Retrieves item attributes.
5363  *
5364  * PARAMETER(S):
5365  * [I] hwnd : window handle
5366  * [IO] lpLVItem : item info
5367  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5368  *           if FALSE, the lpLVItem is a LPLVITEMA.
5369  *
5370  * NOTE:
5371  *   This is the external 'GetItem' interface -- it properly copies
5372  *   the text in the provided buffer.
5373  *
5374  * RETURN:
5375  *   SUCCESS : TRUE
5376  *   FAILURE : FALSE
5377  */
5378 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5379 {
5380     LPWSTR pszText;
5381     BOOL bResult;
5382
5383     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5384         return FALSE;
5385
5386     pszText = lpLVItem->pszText;
5387     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5388     if (bResult && lpLVItem->pszText != pszText)
5389         textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5390     lpLVItem->pszText = pszText;
5391
5392     return bResult;
5393 }
5394
5395
5396 /***
5397  * DESCRIPTION:
5398  * Retrieves the position (upper-left) of the listview control item.
5399  * Note that for LVS_ICON style, the upper-left is that of the icon
5400  * and not the bounding box.
5401  *
5402  * PARAMETER(S):
5403  * [I] infoPtr : valid pointer to the listview structure
5404  * [I] nItem : item index
5405  * [O] lpptPosition : coordinate information
5406  *
5407  * RETURN:
5408  *   SUCCESS : TRUE
5409  *   FAILURE : FALSE
5410  */
5411 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5412 {
5413     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5414     POINT Origin;
5415
5416     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5417
5418     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5419
5420     LISTVIEW_GetOrigin(infoPtr, &Origin);
5421     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5422
5423     if (uView == LVS_ICON)
5424     {
5425         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5426         lpptPosition->y += ICON_TOP_PADDING;
5427     }
5428     lpptPosition->x += Origin.x;
5429     lpptPosition->y += Origin.y;
5430     
5431     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5432     return TRUE;
5433 }
5434
5435
5436 /***
5437  * DESCRIPTION:
5438  * Retrieves the bounding rectangle for a listview control item.
5439  *
5440  * PARAMETER(S):
5441  * [I] infoPtr : valid pointer to the listview structure
5442  * [I] nItem : item index
5443  * [IO] lprc : bounding rectangle coordinates
5444  *     lprc->left specifies the portion of the item for which the bounding
5445  *     rectangle will be retrieved.
5446  *
5447  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5448  *        including the icon and label.
5449  *         *
5450  *         * For LVS_ICON
5451  *         * Experiment shows that native control returns:
5452  *         *  width = min (48, length of text line)
5453  *         *    .left = position.x - (width - iconsize.cx)/2
5454  *         *    .right = .left + width
5455  *         *  height = #lines of text * ntmHeight + icon height + 8
5456  *         *    .top = position.y - 2
5457  *         *    .bottom = .top + height
5458  *         *  separation between items .y = itemSpacing.cy - height
5459  *         *                           .x = itemSpacing.cx - width
5460  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5461  *         *
5462  *         * For LVS_ICON
5463  *         * Experiment shows that native control returns:
5464  *         *  width = iconSize.cx + 16
5465  *         *    .left = position.x - (width - iconsize.cx)/2
5466  *         *    .right = .left + width
5467  *         *  height = iconSize.cy + 4
5468  *         *    .top = position.y - 2
5469  *         *    .bottom = .top + height
5470  *         *  separation between items .y = itemSpacing.cy - height
5471  *         *                           .x = itemSpacing.cx - width
5472  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5473  *         *
5474  *         * For LVS_ICON
5475  *         * Experiment shows that native control returns:
5476  *         *  width = text length
5477  *         *    .left = position.x - width/2
5478  *         *    .right = .left + width
5479  *         *  height = ntmH * linecount + 2
5480  *         *    .top = position.y + iconSize.cy + 6
5481  *         *    .bottom = .top + height
5482  *         *  separation between items .y = itemSpacing.cy - height
5483  *         *                           .x = itemSpacing.cx - width
5484  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5485  *      rectangles, but excludes columns in report view.
5486  *
5487  * RETURN:
5488  *   SUCCESS : TRUE
5489  *   FAILURE : FALSE
5490  *
5491  * NOTES
5492  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5493  *   upon whether the window has the focus currently and on whether the item
5494  *   is the one with the focus.  Ensure that the control's record of which
5495  *   item has the focus agrees with the items' records.
5496  */
5497 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5498 {
5499     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5500     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5501     BOOL doLabel = TRUE, oversizedBox = FALSE;
5502     POINT Position, Origin;
5503     LVITEMW lvItem;
5504     RECT label_rect;
5505
5506     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5507
5508     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5509
5510     LISTVIEW_GetOrigin(infoPtr, &Origin);
5511     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5512
5513     /* Be smart and try to figure out the minimum we have to do */
5514     if (lprc->left == LVIR_ICON) doLabel = FALSE;
5515     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5516     if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5517         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5518         oversizedBox = TRUE;
5519
5520     /* get what we need from the item before hand, so we make
5521      * only one request. This can speed up things, if data
5522      * is stored on the app side */
5523     lvItem.mask = 0;
5524     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5525     if (doLabel) lvItem.mask |= LVIF_TEXT;
5526     lvItem.iItem = nItem;
5527     lvItem.iSubItem = 0;
5528     lvItem.pszText = szDispText;
5529     lvItem.cchTextMax = DISP_TEXT_SIZE;
5530     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5531     /* we got the state already up, simulate it here, to avoid a reget */
5532     if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5533     {
5534         lvItem.mask |= LVIF_STATE;
5535         lvItem.stateMask = LVIS_FOCUSED;
5536         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5537     }
5538
5539     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5540         lprc->left = LVIR_BOUNDS;
5541     switch(lprc->left)
5542     {
5543     case LVIR_ICON:
5544         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5545         break;
5546
5547     case LVIR_LABEL:
5548         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5549         break;
5550
5551     case LVIR_BOUNDS:
5552         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5553         break;
5554
5555     case LVIR_SELECTBOUNDS:
5556         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5557         UnionRect(lprc, lprc, &label_rect);
5558         break;
5559
5560     default:
5561         WARN("Unknown value: %ld\n", lprc->left);
5562         return FALSE;
5563     }
5564
5565     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5566
5567     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5568
5569     return TRUE;
5570 }
5571
5572 /***
5573  * DESCRIPTION:
5574  * Retrieves the spacing between listview control items.
5575  *
5576  * PARAMETER(S):
5577  * [I] infoPtr : valid pointer to the listview structure
5578  * [IO] lprc : rectangle to receive the output
5579  *             on input, lprc->top = nSubItem
5580  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5581  * 
5582  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5583  *       not only those of the first column.
5584  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5585  * 
5586  * RETURN:
5587  *     TRUE: success
5588  *     FALSE: failure
5589  */
5590 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5591 {
5592     POINT Position;
5593     LVITEMW lvItem;
5594     INT nColumn = lprc->top;
5595     
5596     if (!lprc) return FALSE;
5597
5598     TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5599     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5600     if (lprc->top == 0)
5601         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5602
5603     if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5604
5605     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5606
5607     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5608
5609     lvItem.mask = 0;
5610     lvItem.iItem = nItem;
5611     lvItem.iSubItem = nColumn;
5612     
5613     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5614     switch(lprc->left)
5615     {
5616     case LVIR_ICON:
5617         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5618         break;
5619
5620     case LVIR_LABEL:
5621     case LVIR_BOUNDS:
5622         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5623         break;
5624
5625     default:
5626         ERR("Unknown bounds=%ld\n", lprc->left);
5627         return FALSE;
5628     }
5629
5630     OffsetRect(lprc, Position.x, Position.y);
5631     return TRUE;
5632 }
5633
5634
5635 /***
5636  * DESCRIPTION:
5637  * Retrieves the width of a label.
5638  *
5639  * PARAMETER(S):
5640  * [I] infoPtr : valid pointer to the listview structure
5641  *
5642  * RETURN:
5643  *   SUCCESS : string width (in pixels)
5644  *   FAILURE : zero
5645  */
5646 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5647 {
5648     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5649     LVITEMW lvItem;
5650
5651     TRACE("(nItem=%d)\n", nItem);
5652
5653     lvItem.mask = LVIF_TEXT;
5654     lvItem.iItem = nItem;
5655     lvItem.iSubItem = 0;
5656     lvItem.pszText = szDispText;
5657     lvItem.cchTextMax = DISP_TEXT_SIZE;
5658     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5659   
5660     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5661 }
5662
5663 /***
5664  * DESCRIPTION:
5665  * Retrieves the spacing between listview control items.
5666  *
5667  * PARAMETER(S):
5668  * [I] infoPtr : valid pointer to the listview structure
5669  * [I] bSmall : flag for small or large icon
5670  *
5671  * RETURN:
5672  * Horizontal + vertical spacing
5673  */
5674 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5675 {
5676   LONG lResult;
5677
5678   if (!bSmall)
5679   {
5680     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5681   }
5682   else
5683   {
5684     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5685       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5686     else
5687       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5688   }
5689   return lResult;
5690 }
5691
5692 /***
5693  * DESCRIPTION:
5694  * Retrieves the state of a listview control item.
5695  *
5696  * PARAMETER(S):
5697  * [I] infoPtr : valid pointer to the listview structure
5698  * [I] nItem : item index
5699  * [I] uMask : state mask
5700  *
5701  * RETURN:
5702  * State specified by the mask.
5703  */
5704 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5705 {
5706     LVITEMW lvItem;
5707
5708     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5709
5710     lvItem.iItem = nItem;
5711     lvItem.iSubItem = 0;
5712     lvItem.mask = LVIF_STATE;
5713     lvItem.stateMask = uMask;
5714     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5715
5716     return lvItem.state & uMask;
5717 }
5718
5719 /***
5720  * DESCRIPTION:
5721  * Retrieves the text of a listview control item or subitem.
5722  *
5723  * PARAMETER(S):
5724  * [I] hwnd : window handle
5725  * [I] nItem : item index
5726  * [IO] lpLVItem : item information
5727  * [I] isW :  TRUE if lpLVItem is Unicode
5728  *
5729  * RETURN:
5730  *   SUCCESS : string length
5731  *   FAILURE : 0
5732  */
5733 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5734 {
5735     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5736
5737     lpLVItem->mask = LVIF_TEXT;
5738     lpLVItem->iItem = nItem;
5739     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5740
5741     return textlenT(lpLVItem->pszText, isW);
5742 }
5743
5744 /***
5745  * DESCRIPTION:
5746  * Searches for an item based on properties + relationships.
5747  *
5748  * PARAMETER(S):
5749  * [I] infoPtr : valid pointer to the listview structure
5750  * [I] nItem : item index
5751  * [I] uFlags : relationship flag
5752  *
5753  * RETURN:
5754  *   SUCCESS : item index
5755  *   FAILURE : -1
5756  */
5757 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5758 {
5759     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5760     UINT uMask = 0;
5761     LVFINDINFOW lvFindInfo;
5762     INT nCountPerColumn;
5763     INT nCountPerRow;
5764     INT i;
5765
5766     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5767     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5768
5769     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5770
5771     if (uFlags & LVNI_CUT)
5772       uMask |= LVIS_CUT;
5773
5774     if (uFlags & LVNI_DROPHILITED)
5775       uMask |= LVIS_DROPHILITED;
5776
5777     if (uFlags & LVNI_FOCUSED)
5778       uMask |= LVIS_FOCUSED;
5779
5780     if (uFlags & LVNI_SELECTED)
5781       uMask |= LVIS_SELECTED;
5782
5783     /* if we're asked for the focused item, that's only one, 
5784      * so it's worth optimizing */
5785     if (uFlags & LVNI_FOCUSED)
5786     {
5787         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5788         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5789     }
5790     
5791     if (uFlags & LVNI_ABOVE)
5792     {
5793       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5794       {
5795         while (nItem >= 0)
5796         {
5797           nItem--;
5798           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5799             return nItem;
5800         }
5801       }
5802       else
5803       {
5804         /* Special case for autoarrange - move 'til the top of a list */
5805         if (is_autoarrange(infoPtr))
5806         {
5807           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5808           while (nItem - nCountPerRow >= 0)
5809           {
5810             nItem -= nCountPerRow;
5811             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5812               return nItem;
5813           }
5814           return -1;
5815         }
5816         lvFindInfo.flags = LVFI_NEARESTXY;
5817         lvFindInfo.vkDirection = VK_UP;
5818         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5819         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5820         {
5821           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5822             return nItem;
5823         }
5824       }
5825     }
5826     else if (uFlags & LVNI_BELOW)
5827     {
5828       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5829       {
5830         while (nItem < infoPtr->nItemCount)
5831         {
5832           nItem++;
5833           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5834             return nItem;
5835         }
5836       }
5837       else
5838       {
5839         /* Special case for autoarrange - move 'til the bottom of a list */
5840         if (is_autoarrange(infoPtr))
5841         {
5842           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5843           while (nItem + nCountPerRow < infoPtr->nItemCount )
5844           {
5845             nItem += nCountPerRow;
5846             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5847               return nItem;
5848           }
5849           return -1;
5850         }
5851         lvFindInfo.flags = LVFI_NEARESTXY;
5852         lvFindInfo.vkDirection = VK_DOWN;
5853         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5854         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5855         {
5856           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5857             return nItem;
5858         }
5859       }
5860     }
5861     else if (uFlags & LVNI_TOLEFT)
5862     {
5863       if (uView == LVS_LIST)
5864       {
5865         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5866         while (nItem - nCountPerColumn >= 0)
5867         {
5868           nItem -= nCountPerColumn;
5869           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5870             return nItem;
5871         }
5872       }
5873       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5874       {
5875         /* Special case for autoarrange - move 'ti the beginning of a row */
5876         if (is_autoarrange(infoPtr))
5877         {
5878           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5879           while (nItem % nCountPerRow > 0)
5880           {
5881             nItem --;
5882             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5883               return nItem;
5884           }
5885           return -1;
5886         }
5887         lvFindInfo.flags = LVFI_NEARESTXY;
5888         lvFindInfo.vkDirection = VK_LEFT;
5889         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5890         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5891         {
5892           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5893             return nItem;
5894         }
5895       }
5896     }
5897     else if (uFlags & LVNI_TORIGHT)
5898     {
5899       if (uView == LVS_LIST)
5900       {
5901         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5902         while (nItem + nCountPerColumn < infoPtr->nItemCount)
5903         {
5904           nItem += nCountPerColumn;
5905           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5906             return nItem;
5907         }
5908       }
5909       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5910       {
5911         /* Special case for autoarrange - move 'til the end of a row */
5912         if (is_autoarrange(infoPtr))
5913         {
5914           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5915           while (nItem % nCountPerRow < nCountPerRow - 1 )
5916           {
5917             nItem ++;
5918             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5919               return nItem;
5920           }
5921           return -1;
5922         }
5923         lvFindInfo.flags = LVFI_NEARESTXY;
5924         lvFindInfo.vkDirection = VK_RIGHT;
5925         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5926         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5927         {
5928           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5929             return nItem;
5930         }
5931       }
5932     }
5933     else
5934     {
5935       nItem++;
5936
5937       /* search by index */
5938       for (i = nItem; i < infoPtr->nItemCount; i++)
5939       {
5940         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5941           return i;
5942       }
5943     }
5944
5945     return -1;
5946 }
5947
5948 /* LISTVIEW_GetNumberOfWorkAreas */
5949
5950 /***
5951  * DESCRIPTION:
5952  * Retrieves the origin coordinates when in icon or small icon display mode.
5953  *
5954  * PARAMETER(S):
5955  * [I] infoPtr : valid pointer to the listview structure
5956  * [O] lpptOrigin : coordinate information
5957  *
5958  * RETURN:
5959  *   None.
5960  */
5961 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5962 {
5963     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5964     INT nHorzPos = 0, nVertPos = 0;
5965     SCROLLINFO scrollInfo;
5966
5967     scrollInfo.cbSize = sizeof(SCROLLINFO);    
5968     scrollInfo.fMask = SIF_POS;
5969     
5970     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5971         nHorzPos = scrollInfo.nPos;
5972     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5973         nVertPos = scrollInfo.nPos;
5974
5975     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5976
5977     lpptOrigin->x = infoPtr->rcList.left;
5978     lpptOrigin->y = infoPtr->rcList.top;
5979     if (uView == LVS_LIST)
5980         nHorzPos *= infoPtr->nItemWidth;
5981     else if (uView == LVS_REPORT)
5982         nVertPos *= infoPtr->nItemHeight;
5983     
5984     lpptOrigin->x -= nHorzPos;
5985     lpptOrigin->y -= nVertPos;
5986
5987     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
5988 }
5989
5990 /***
5991  * DESCRIPTION:
5992  * Retrieves the width of a string.
5993  *
5994  * PARAMETER(S):
5995  * [I] hwnd : window handle
5996  * [I] lpszText : text string to process
5997  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5998  *
5999  * RETURN:
6000  *   SUCCESS : string width (in pixels)
6001  *   FAILURE : zero
6002  */
6003 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6004 {
6005     SIZE stringSize;
6006     
6007     stringSize.cx = 0;    
6008     if (is_textT(lpszText, isW))
6009     {
6010         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6011         HDC hdc = GetDC(infoPtr->hwndSelf);
6012         HFONT hOldFont = SelectObject(hdc, hFont);
6013
6014         if (isW)
6015             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6016         else
6017             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6018         SelectObject(hdc, hOldFont);
6019         ReleaseDC(infoPtr->hwndSelf, hdc);
6020     }
6021     return stringSize.cx;
6022 }
6023
6024 /***
6025  * DESCRIPTION:
6026  * Determines which listview item is located at the specified position.
6027  *
6028  * PARAMETER(S):
6029  * [I] infoPtr : valid pointer to the listview structure
6030  * [IO] lpht : hit test information
6031  * [I] subitem : fill out iSubItem.
6032  * [I] select : return the index only if the hit selects the item
6033  *
6034  * NOTE:
6035  * (mm 20001022): We must not allow iSubItem to be touched, for
6036  * an app might pass only a structure with space up to iItem!
6037  * (MS Office 97 does that for instance in the file open dialog)
6038  * 
6039  * RETURN:
6040  *   SUCCESS : item index
6041  *   FAILURE : -1
6042  */
6043 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6044 {
6045     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6046     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6047     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6048     POINT Origin, Position, opt;
6049     LVITEMW lvItem;
6050     ITERATOR i;
6051     INT iItem;
6052     
6053     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6054     
6055     lpht->flags = 0;
6056     lpht->iItem = -1;
6057     if (subitem) lpht->iSubItem = 0;
6058
6059     if (infoPtr->rcList.left > lpht->pt.x)
6060         lpht->flags |= LVHT_TOLEFT;
6061     else if (infoPtr->rcList.right < lpht->pt.x)
6062         lpht->flags |= LVHT_TORIGHT;
6063     
6064     if (infoPtr->rcList.top > lpht->pt.y)
6065         lpht->flags |= LVHT_ABOVE;
6066     else if (infoPtr->rcList.bottom < lpht->pt.y)
6067         lpht->flags |= LVHT_BELOW;
6068
6069     TRACE("lpht->flags=0x%x\n", lpht->flags);
6070     if (lpht->flags) return -1;
6071
6072     lpht->flags |= LVHT_NOWHERE;
6073
6074     LISTVIEW_GetOrigin(infoPtr, &Origin);
6075    
6076     /* first deal with the large items */
6077     rcSearch.left = lpht->pt.x;
6078     rcSearch.top = lpht->pt.y;
6079     rcSearch.right = rcSearch.left + 1;
6080     rcSearch.bottom = rcSearch.top + 1;
6081     
6082     iterator_frameditems(&i, infoPtr, &rcSearch);
6083     iterator_next(&i); /* go to first item in the sequence */
6084     iItem = i.nItem;
6085     iterator_destroy(&i);
6086    
6087     TRACE("lpht->iItem=%d\n", iItem); 
6088     if (iItem == -1) return -1;
6089
6090     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6091     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6092     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6093     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6094     lvItem.iItem = iItem;
6095     lvItem.iSubItem = 0;
6096     lvItem.pszText = szDispText;
6097     lvItem.cchTextMax = DISP_TEXT_SIZE;
6098     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6099     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED; 
6100     
6101     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6102     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6103     opt.x = lpht->pt.x - Position.x - Origin.x;
6104     opt.y = lpht->pt.y - Position.y - Origin.y;
6105     
6106     if (uView == LVS_REPORT)
6107         rcBounds = rcBox;
6108     else
6109         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6110     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6111     if (!PtInRect(&rcBounds, opt)) return -1;
6112
6113     if (PtInRect(&rcIcon, opt))
6114         lpht->flags |= LVHT_ONITEMICON;
6115     else if (PtInRect(&rcLabel, opt))
6116         lpht->flags |= LVHT_ONITEMLABEL;
6117     else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6118         lpht->flags |= LVHT_ONITEMSTATEICON;
6119     if (lpht->flags & LVHT_ONITEM)
6120         lpht->flags &= ~LVHT_NOWHERE;
6121    
6122     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6123     if (uView == LVS_REPORT && subitem)
6124     {
6125         INT j;
6126
6127         rcBounds.right = rcBounds.left;
6128         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6129         {
6130             rcBounds.left = rcBounds.right;
6131             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6132             if (PtInRect(&rcBounds, opt))
6133             {
6134                 lpht->iSubItem = j;
6135                 break;
6136             }
6137         }
6138     }
6139
6140     if (select && !(uView == LVS_REPORT &&
6141                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6142                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6143     {
6144         if (uView == LVS_REPORT)
6145         {
6146             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6147             UnionRect(&rcBounds, &rcBounds, &rcState);
6148         }
6149         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6150     }
6151     return lpht->iItem = iItem;
6152 }
6153
6154
6155 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6156    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6157    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6158    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6159    their own sort proc. when sending LVM_SORTITEMS.
6160 */
6161 /* Platform SDK:
6162     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6163         if:
6164           LVS_SORTXXX must be specified,
6165           LVS_OWNERDRAW is not set,
6166           <item>.pszText is not LPSTR_TEXTCALLBACK.
6167
6168     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6169     are sorted based on item text..."
6170 */
6171 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6172 {
6173     ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6174     ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6175     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
6176
6177     /* if we're sorting descending, negate the return value */
6178     return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6179 }
6180
6181 /***
6182  * DESCRIPTION:
6183  * Inserts a new item in the listview control.
6184  *
6185  * PARAMETER(S):
6186  * [I] infoPtr : valid pointer to the listview structure
6187  * [I] lpLVItem : item information
6188  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6189  *
6190  * RETURN:
6191  *   SUCCESS : new item index
6192  *   FAILURE : -1
6193  */
6194 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6195 {
6196     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6197     INT nItem;
6198     HDPA hdpaSubItems;
6199     NMLISTVIEW nmlv;
6200     ITEM_INFO *lpItem;
6201     BOOL is_sorted, has_changed;
6202     LVITEMW item;
6203     HWND hwndSelf = infoPtr->hwndSelf;
6204
6205     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6206
6207     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6208
6209     /* make sure it's an item, and not a subitem; cannot insert a subitem */
6210     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6211
6212     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6213
6214     if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6215     
6216     /* insert item in listview control data structure */
6217     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6218     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6219
6220     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6221                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6222
6223     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6224     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6225     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6226     if (nItem == -1) goto fail;
6227     infoPtr->nItemCount++;
6228
6229     /* shift indices first so they don't get tangled */
6230     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6231
6232     /* set the item attributes */
6233     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6234     {
6235         /* full size structure expected - _WIN32IE >= 0x560 */
6236         item = *lpLVItem;
6237     }
6238     else if (lpLVItem->mask & LVIF_INDENT)
6239     {
6240         /* indent member expected - _WIN32IE >= 0x300 */
6241         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6242     }
6243     else
6244     {
6245         /* minimal structure expected */
6246         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6247     }
6248     item.iItem = nItem;
6249     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6250     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6251
6252     /* if we're sorted, sort the list, and update the index */
6253     if (is_sorted)
6254     {
6255         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6256         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6257         assert(nItem != -1);
6258     }
6259
6260     /* make room for the position, if we are in the right mode */
6261     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6262     {
6263         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6264             goto undo;
6265         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6266         {
6267             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6268             goto undo;
6269         }
6270     }
6271     
6272     /* send LVN_INSERTITEM notification */
6273     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6274     nmlv.iItem = nItem;
6275     nmlv.lParam = lpItem->lParam;
6276     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6277     if (!IsWindow(hwndSelf))
6278         return -1;
6279
6280     /* align items (set position of each item) */
6281     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6282     {
6283         POINT pt;
6284
6285         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6286             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6287         else
6288             LISTVIEW_NextIconPosTop(infoPtr, &pt);
6289
6290         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6291     }
6292
6293     /* now is the invalidation fun */
6294     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6295     return nItem;
6296
6297 undo:
6298     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6299     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6300     infoPtr->nItemCount--;
6301 fail:
6302     DPA_DeletePtr(hdpaSubItems, 0);
6303     DPA_Destroy (hdpaSubItems);
6304     Free (lpItem);
6305     return -1;
6306 }
6307
6308 /***
6309  * DESCRIPTION:
6310  * Redraws a range of items.
6311  *
6312  * PARAMETER(S):
6313  * [I] infoPtr : valid pointer to the listview structure
6314  * [I] nFirst : first item
6315  * [I] nLast : last item
6316  *
6317  * RETURN:
6318  *   SUCCESS : TRUE
6319  *   FAILURE : FALSE
6320  */
6321 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6322 {
6323     INT i;
6324  
6325     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
6326         max(nFirst, nLast) >= infoPtr->nItemCount)
6327         return FALSE;
6328     
6329     for (i = nFirst; i <= nLast; i++)
6330         LISTVIEW_InvalidateItem(infoPtr, i);
6331
6332     return TRUE;
6333 }
6334
6335 /***
6336  * DESCRIPTION:
6337  * Scroll the content of a listview.
6338  *
6339  * PARAMETER(S):
6340  * [I] infoPtr : valid pointer to the listview structure
6341  * [I] dx : horizontal scroll amount in pixels
6342  * [I] dy : vertical scroll amount in pixels
6343  *
6344  * RETURN:
6345  *   SUCCESS : TRUE
6346  *   FAILURE : FALSE
6347  *
6348  * COMMENTS:
6349  *  If the control is in report mode (LVS_REPORT) the control can
6350  *  be scrolled only in line increments. "dy" will be rounded to the
6351  *  nearest number of pixels that are a whole line. Ex: if line height
6352  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6353  *  is passed the the scroll will be 0.  (per MSDN 7/2002)
6354  *
6355  *  For:  (per experimentaion with native control and CSpy ListView)
6356  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
6357  *                    dx ignored
6358  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
6359  *                    dx ignored
6360  *     LVS_LIST       dx=1 = 1 column (horizontal only)
6361  *                           but will only scroll 1 column per message
6362  *                           no matter what the value.
6363  *                    dy must be 0 or FALSE returned.
6364  *     LVS_REPORT     dx=1 = 1 pixel
6365  *                    dy=  see above
6366  *
6367  */
6368 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6369 {
6370     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6371     case LVS_REPORT:
6372         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6373         dy /= infoPtr->nItemHeight;
6374         break;
6375     case LVS_LIST:
6376         if (dy != 0) return FALSE;
6377         break;
6378     default: /* icon */
6379         dx = 0;
6380         break;
6381     }   
6382
6383     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6384     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6385   
6386     return TRUE;
6387 }
6388
6389 /***
6390  * DESCRIPTION:
6391  * Sets the background color.
6392  *
6393  * PARAMETER(S):
6394  * [I] infoPtr : valid pointer to the listview structure
6395  * [I] clrBk : background color
6396  *
6397  * RETURN:
6398  *   SUCCESS : TRUE
6399  *   FAILURE : FALSE
6400  */
6401 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6402 {
6403     TRACE("(clrBk=%lx)\n", clrBk);
6404
6405     if(infoPtr->clrBk != clrBk) {
6406         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6407         infoPtr->clrBk = clrBk;
6408         if (clrBk == CLR_NONE)
6409             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6410         else
6411             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6412         LISTVIEW_InvalidateList(infoPtr);
6413     }
6414
6415    return TRUE;
6416 }
6417
6418 /* LISTVIEW_SetBkImage */
6419
6420 /*** Helper for {Insert,Set}ColumnT *only* */
6421 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6422 {
6423     if (lpColumn->mask & LVCF_FMT)
6424     {
6425         /* format member is valid */
6426         lphdi->mask |= HDI_FORMAT;
6427
6428         /* set text alignment (leftmost column must be left-aligned) */
6429         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6430             lphdi->fmt |= HDF_LEFT;
6431         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6432             lphdi->fmt |= HDF_RIGHT;
6433         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6434             lphdi->fmt |= HDF_CENTER;
6435
6436         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6437             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6438
6439         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6440         {
6441             lphdi->fmt |= HDF_IMAGE;
6442             lphdi->iImage = I_IMAGECALLBACK;
6443         }
6444     }
6445
6446     if (lpColumn->mask & LVCF_WIDTH)
6447     {
6448         lphdi->mask |= HDI_WIDTH;
6449         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6450         {
6451             /* make it fill the remainder of the controls width */
6452             RECT rcHeader;
6453             INT item_index;
6454
6455             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6456             {
6457                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6458                 lphdi->cxy += rcHeader.right - rcHeader.left;
6459             }
6460
6461             /* retrieve the layout of the header */
6462             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6463             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6464
6465             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6466         }
6467         else
6468             lphdi->cxy = lpColumn->cx;
6469     }
6470
6471     if (lpColumn->mask & LVCF_TEXT)
6472     {
6473         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6474         lphdi->fmt |= HDF_STRING;
6475         lphdi->pszText = lpColumn->pszText;
6476         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6477     }
6478
6479     if (lpColumn->mask & LVCF_IMAGE)
6480     {
6481         lphdi->mask |= HDI_IMAGE;
6482         lphdi->iImage = lpColumn->iImage;
6483     }
6484
6485     if (lpColumn->mask & LVCF_ORDER)
6486     {
6487         lphdi->mask |= HDI_ORDER;
6488         lphdi->iOrder = lpColumn->iOrder;
6489     }
6490 }
6491
6492
6493 /***
6494  * DESCRIPTION:
6495  * Inserts a new column.
6496  *
6497  * PARAMETER(S):
6498  * [I] infoPtr : valid pointer to the listview structure
6499  * [I] nColumn : column index
6500  * [I] lpColumn : column information
6501  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6502  *
6503  * RETURN:
6504  *   SUCCESS : new column index
6505  *   FAILURE : -1
6506  */
6507 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6508                                   const LVCOLUMNW *lpColumn, BOOL isW)
6509 {
6510     COLUMN_INFO *lpColumnInfo;
6511     INT nNewColumn;
6512     HDITEMW hdi;
6513
6514     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6515
6516     if (!lpColumn || nColumn < 0) return -1;
6517     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6518     
6519     ZeroMemory(&hdi, sizeof(HDITEMW));
6520     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6521
6522     /* insert item in header control */
6523     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6524                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6525                               (WPARAM)nColumn, (LPARAM)&hdi);
6526     if (nNewColumn == -1) return -1;
6527     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6528    
6529     /* create our own column info */ 
6530     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6531     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6532
6533     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6534     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6535
6536     /* now we have to actually adjust the data */
6537     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6538     {
6539         SUBITEM_INFO *lpSubItem;
6540         HDPA hdpaSubItems;
6541         INT nItem, i;
6542         
6543         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6544         {
6545             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6546             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6547             {
6548                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6549                 if (lpSubItem->iSubItem >= nNewColumn)
6550                     lpSubItem->iSubItem++;
6551             }
6552         }
6553     }
6554
6555     /* make space for the new column */
6556     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6557     
6558     return nNewColumn;
6559
6560 fail:
6561     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6562     if (lpColumnInfo)
6563     {
6564         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6565         Free(lpColumnInfo);
6566     }
6567     return -1;
6568 }
6569
6570 /***
6571  * DESCRIPTION:
6572  * Sets the attributes of a header item.
6573  *
6574  * PARAMETER(S):
6575  * [I] infoPtr : valid pointer to the listview structure
6576  * [I] nColumn : column index
6577  * [I] lpColumn : column attributes
6578  * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6579  *
6580  * RETURN:
6581  *   SUCCESS : TRUE
6582  *   FAILURE : FALSE
6583  */
6584 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6585                                 const LVCOLUMNW *lpColumn, BOOL isW)
6586 {
6587     HDITEMW hdi, hdiget;
6588     BOOL bResult;
6589
6590     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6591     
6592     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6593
6594     ZeroMemory(&hdi, sizeof(HDITEMW));
6595     if (lpColumn->mask & LVCF_FMT)
6596     {
6597         hdi.mask |= HDI_FORMAT;
6598         hdiget.mask = HDI_FORMAT;
6599         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6600             hdi.fmt = hdiget.fmt & HDF_STRING;
6601     }
6602     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6603
6604     /* set header item attributes */
6605     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6606     if (!bResult) return FALSE;
6607
6608     if (lpColumn->mask & LVCF_FMT)
6609     {
6610         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6611         int oldFmt = lpColumnInfo->fmt;
6612         
6613         lpColumnInfo->fmt = lpColumn->fmt;
6614         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6615         {
6616             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6617             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6618         }
6619     }
6620
6621     return TRUE;
6622 }
6623
6624 /***
6625  * DESCRIPTION:
6626  * Sets the column order array
6627  *
6628  * PARAMETERS:
6629  * [I] infoPtr : valid pointer to the listview structure
6630  * [I] iCount : number of elements in column order array
6631  * [I] lpiArray : pointer to column order array
6632  *
6633  * RETURN:
6634  *   SUCCESS : TRUE
6635  *   FAILURE : FALSE
6636  */
6637 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6638 {
6639   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6640
6641   if (!lpiArray)
6642     return FALSE;
6643
6644   return TRUE;
6645 }
6646
6647
6648 /***
6649  * DESCRIPTION:
6650  * Sets the width of a column
6651  *
6652  * PARAMETERS:
6653  * [I] infoPtr : valid pointer to the listview structure
6654  * [I] nColumn : column index
6655  * [I] cx : column width
6656  *
6657  * RETURN:
6658  *   SUCCESS : TRUE
6659  *   FAILURE : FALSE
6660  */
6661 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6662 {
6663     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6664     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6665     INT max_cx = 0;
6666     HDITEMW hdi;
6667
6668     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6669
6670     /* set column width only if in report or list mode */
6671     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6672
6673     /* take care of invalid cx values */
6674     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6675     else if (uView == LVS_LIST && cx < 1) return FALSE;
6676
6677     /* resize all columns if in LVS_LIST mode */
6678     if(uView == LVS_LIST) 
6679     {
6680         infoPtr->nItemWidth = cx;
6681         LISTVIEW_InvalidateList(infoPtr);
6682         return TRUE;
6683     }
6684
6685     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6686
6687     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6688     {
6689         INT nLabelWidth;
6690         LVITEMW lvItem;
6691
6692         lvItem.mask = LVIF_TEXT;        
6693         lvItem.iItem = 0;
6694         lvItem.iSubItem = nColumn;
6695         lvItem.pszText = szDispText;
6696         lvItem.cchTextMax = DISP_TEXT_SIZE;
6697         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6698         {
6699             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6700             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6701             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6702         }
6703         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6704             max_cx += infoPtr->iconSize.cx;
6705         max_cx += TRAILING_LABEL_PADDING;
6706     }
6707
6708     /* autosize based on listview items width */
6709     if(cx == LVSCW_AUTOSIZE)
6710         cx = max_cx;
6711     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6712     {
6713         /* if iCol is the last column make it fill the remainder of the controls width */
6714         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
6715         {
6716             RECT rcHeader;
6717             POINT Origin;
6718
6719             LISTVIEW_GetOrigin(infoPtr, &Origin);
6720             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6721
6722             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6723         }
6724         else
6725         {
6726             /* Despite what the MS docs say, if this is not the last
6727                column, then MS resizes the column to the width of the
6728                largest text string in the column, including headers
6729                and items. This is different from LVSCW_AUTOSIZE in that
6730                LVSCW_AUTOSIZE ignores the header string length. */
6731             cx = 0;
6732
6733             /* retrieve header text */
6734             hdi.mask = HDI_TEXT;
6735             hdi.cchTextMax = DISP_TEXT_SIZE;
6736             hdi.pszText = szDispText;
6737             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6738             {
6739                 HDC hdc = GetDC(infoPtr->hwndSelf);
6740                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6741                 SIZE size;
6742
6743                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6744                     cx = size.cx + TRAILING_HEADER_PADDING;
6745                 /* FIXME: Take into account the header image, if one is present */
6746                 SelectObject(hdc, old_font);
6747                 ReleaseDC(infoPtr->hwndSelf, hdc);
6748             }
6749             cx = max (cx, max_cx);
6750         }
6751     }
6752
6753     if (cx < 0) return FALSE;
6754
6755     /* call header to update the column change */
6756     hdi.mask = HDI_WIDTH;
6757     hdi.cxy = cx;
6758     TRACE("hdi.cxy=%d\n", hdi.cxy);
6759     return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6760 }
6761
6762 /***
6763  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
6764  *
6765  */
6766 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6767 {
6768     HDC hdc_wnd, hdc;
6769     HBITMAP hbm_im, hbm_mask, hbm_orig;
6770     RECT rc;
6771     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6772     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6773     HIMAGELIST himl;
6774
6775     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6776                             ILC_COLOR | ILC_MASK, 2, 2);
6777     hdc_wnd = GetDC(infoPtr->hwndSelf);
6778     hdc = CreateCompatibleDC(hdc_wnd);
6779     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6780     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6781     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6782
6783     rc.left = rc.top = 0;
6784     rc.right = GetSystemMetrics(SM_CXSMICON);
6785     rc.bottom = GetSystemMetrics(SM_CYSMICON);
6786
6787     hbm_orig = SelectObject(hdc, hbm_mask);
6788     FillRect(hdc, &rc, hbr_white);
6789     InflateRect(&rc, -3, -3);
6790     FillRect(hdc, &rc, hbr_black);
6791
6792     SelectObject(hdc, hbm_im);
6793     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6794     SelectObject(hdc, hbm_orig);
6795     ImageList_Add(himl, hbm_im, hbm_mask); 
6796
6797     SelectObject(hdc, hbm_im);
6798     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6799     SelectObject(hdc, hbm_orig);
6800     ImageList_Add(himl, hbm_im, hbm_mask);
6801
6802     DeleteObject(hbm_mask);
6803     DeleteObject(hbm_im);
6804     DeleteDC(hdc);
6805
6806     return himl;
6807 }
6808
6809 /***
6810  * DESCRIPTION:
6811  * Sets the extended listview style.
6812  *
6813  * PARAMETERS:
6814  * [I] infoPtr : valid pointer to the listview structure
6815  * [I] dwMask : mask
6816  * [I] dwStyle : style
6817  *
6818  * RETURN:
6819  *   SUCCESS : previous style
6820  *   FAILURE : 0
6821  */
6822 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6823 {
6824     DWORD dwOldStyle = infoPtr->dwLvExStyle;
6825
6826     /* set new style */
6827     if (dwMask)
6828         infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6829     else
6830         infoPtr->dwLvExStyle = dwStyle;
6831
6832     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6833     {
6834         HIMAGELIST himl = 0;
6835         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6836             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6837         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6838     }
6839
6840     return dwOldStyle;
6841 }
6842
6843 /***
6844  * DESCRIPTION:
6845  * Sets the new hot cursor used during hot tracking and hover selection.
6846  *
6847  * PARAMETER(S):
6848  * [I] infoPtr : valid pointer to the listview structure
6849  * [I] hCursor : the new hot cursor handle
6850  *
6851  * RETURN:
6852  * Returns the previous hot cursor
6853  */
6854 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6855 {
6856     HCURSOR oldCursor = infoPtr->hHotCursor;
6857     
6858     infoPtr->hHotCursor = hCursor;
6859
6860     return oldCursor;
6861 }
6862
6863
6864 /***
6865  * DESCRIPTION:
6866  * Sets the hot item index.
6867  *
6868  * PARAMETERS:
6869  * [I] infoPtr : valid pointer to the listview structure
6870  * [I] iIndex : index
6871  *
6872  * RETURN:
6873  *   SUCCESS : previous hot item index
6874  *   FAILURE : -1 (no hot item)
6875  */
6876 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6877 {
6878     INT iOldIndex = infoPtr->nHotItem;
6879     
6880     infoPtr->nHotItem = iIndex;
6881     
6882     return iOldIndex;
6883 }
6884
6885
6886 /***
6887  * DESCRIPTION:
6888  * Sets the amount of time the cursor must hover over an item before it is selected.
6889  *
6890  * PARAMETER(S):
6891  * [I] infoPtr : valid pointer to the listview structure
6892  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6893  *
6894  * RETURN:
6895  * Returns the previous hover time
6896  */
6897 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6898 {
6899     DWORD oldHoverTime = infoPtr->dwHoverTime;
6900     
6901     infoPtr->dwHoverTime = dwHoverTime;
6902     
6903     return oldHoverTime;
6904 }
6905
6906 /***
6907  * DESCRIPTION:
6908  * Sets spacing for icons of LVS_ICON style.
6909  *
6910  * PARAMETER(S):
6911  * [I] infoPtr : valid pointer to the listview structure
6912  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6913  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6914  *
6915  * RETURN:
6916  *   MAKELONG(oldcx, oldcy)
6917  */
6918 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6919 {
6920     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6921     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6922
6923     TRACE("requested=(%d,%d)\n", cx, cy);
6924     
6925     /* this is supported only for LVS_ICON style */
6926     if (uView != LVS_ICON) return oldspacing;
6927   
6928     /* set to defaults, if instructed to */
6929     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6930     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6931
6932     /* if 0 then compute width
6933      * FIXME: Should scan each item and determine max width of
6934      *        icon or label, then make that the width */
6935     if (cx == 0)
6936         cx = infoPtr->iconSpacing.cx;
6937
6938     /* if 0 then compute height */
6939     if (cy == 0) 
6940         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6941              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6942     
6943
6944     infoPtr->iconSpacing.cx = cx;
6945     infoPtr->iconSpacing.cy = cy;
6946
6947     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6948           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
6949           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6950           infoPtr->ntmHeight);
6951
6952     /* these depend on the iconSpacing */
6953     LISTVIEW_UpdateItemSize(infoPtr);
6954
6955     return oldspacing;
6956 }
6957
6958 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6959 {
6960     INT cx, cy;
6961     
6962     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6963     {
6964         size->cx = cx;
6965         size->cy = cy;
6966     }
6967     else
6968     {
6969         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6970         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6971     }
6972 }
6973
6974 /***
6975  * DESCRIPTION:
6976  * Sets image lists.
6977  *
6978  * PARAMETER(S):
6979  * [I] infoPtr : valid pointer to the listview structure
6980  * [I] nType : image list type
6981  * [I] himl : image list handle
6982  *
6983  * RETURN:
6984  *   SUCCESS : old image list
6985  *   FAILURE : NULL
6986  */
6987 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6988 {
6989     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6990     INT oldHeight = infoPtr->nItemHeight;
6991     HIMAGELIST himlOld = 0;
6992
6993     TRACE("(nType=%d, himl=%p\n", nType, himl);
6994
6995     switch (nType)
6996     {
6997     case LVSIL_NORMAL:
6998         himlOld = infoPtr->himlNormal;
6999         infoPtr->himlNormal = himl;
7000         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7001         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7002     break;
7003
7004     case LVSIL_SMALL:
7005         himlOld = infoPtr->himlSmall;
7006         infoPtr->himlSmall = himl;
7007          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7008     break;
7009
7010     case LVSIL_STATE:
7011         himlOld = infoPtr->himlState;
7012         infoPtr->himlState = himl;
7013         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7014         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7015     break;
7016
7017     default:
7018         ERR("Unknown icon type=%d\n", nType);
7019         return NULL;
7020     }
7021
7022     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7023     if (infoPtr->nItemHeight != oldHeight)
7024         LISTVIEW_UpdateScroll(infoPtr);
7025
7026     return himlOld;
7027 }
7028
7029 /***
7030  * DESCRIPTION:
7031  * Preallocates memory (does *not* set the actual count of items !)
7032  *
7033  * PARAMETER(S):
7034  * [I] infoPtr : valid pointer to the listview structure
7035  * [I] nItems : item count (projected number of items to allocate)
7036  * [I] dwFlags : update flags
7037  *
7038  * RETURN:
7039  *   SUCCESS : TRUE
7040  *   FAILURE : FALSE
7041  */
7042 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7043 {
7044     TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7045
7046     if (infoPtr->dwStyle & LVS_OWNERDATA)
7047     {
7048         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7049         INT nOldCount = infoPtr->nItemCount;
7050
7051         if (nItems < nOldCount)
7052         {
7053             RANGE range = { nItems, nOldCount };
7054             ranges_del(infoPtr->selectionRanges, range);
7055             if (infoPtr->nFocusedItem >= nItems)
7056             {
7057                 infoPtr->nFocusedItem = -1;
7058                 SetRectEmpty(&infoPtr->rcFocus);
7059             }
7060         }
7061
7062         infoPtr->nItemCount = nItems;
7063         LISTVIEW_UpdateScroll(infoPtr);
7064
7065         /* the flags are valid only in ownerdata report and list modes */
7066         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7067
7068         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7069             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7070
7071         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7072             LISTVIEW_InvalidateList(infoPtr);
7073         else
7074         {
7075             INT nFrom, nTo;
7076             POINT Origin;
7077             RECT rcErase;
7078             
7079             LISTVIEW_GetOrigin(infoPtr, &Origin);
7080             nFrom = min(nOldCount, nItems);
7081             nTo = max(nOldCount, nItems);
7082     
7083             if (uView == LVS_REPORT)
7084             {
7085                 rcErase.left = 0;
7086                 rcErase.top = nFrom * infoPtr->nItemHeight;
7087                 rcErase.right = infoPtr->nItemWidth;
7088                 rcErase.bottom = nTo * infoPtr->nItemHeight;
7089                 OffsetRect(&rcErase, Origin.x, Origin.y);
7090                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7091                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7092             }
7093             else /* LVS_LIST */
7094             {
7095                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7096
7097                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7098                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7099                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7100                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7101                 OffsetRect(&rcErase, Origin.x, Origin.y);
7102                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7103                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7104
7105                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7106                 rcErase.top = 0;
7107                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7108                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7109                 OffsetRect(&rcErase, Origin.x, Origin.y);
7110                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7111                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7112             }
7113         }
7114     }
7115     else
7116     {
7117         /* According to MSDN for non-LVS_OWNERDATA this is just
7118          * a performance issue. The control allocates its internal
7119          * data structures for the number of items specified. It
7120          * cuts down on the number of memory allocations. Therefore
7121          * we will just issue a WARN here
7122          */
7123         WARN("for non-ownerdata performance option not implemented.\n");
7124     }
7125
7126     return TRUE;
7127 }
7128
7129 /***
7130  * DESCRIPTION:
7131  * Sets the position of an item.
7132  *
7133  * PARAMETER(S):
7134  * [I] infoPtr : valid pointer to the listview structure
7135  * [I] nItem : item index
7136  * [I] pt : coordinate
7137  *
7138  * RETURN:
7139  *   SUCCESS : TRUE
7140  *   FAILURE : FALSE
7141  */
7142 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7143 {
7144     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7145     POINT Origin;
7146
7147     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7148
7149     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7150         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7151
7152     LISTVIEW_GetOrigin(infoPtr, &Origin);
7153
7154     /* This point value seems to be an undocumented feature.
7155      * The best guess is that it means either at the origin, 
7156      * or at true beginning of the list. I will assume the origin. */
7157     if ((pt.x == -1) && (pt.y == -1))
7158         pt = Origin;
7159     
7160     if (uView == LVS_ICON)
7161     {
7162         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7163         pt.y -= ICON_TOP_PADDING;
7164     }
7165     pt.x -= Origin.x;
7166     pt.y -= Origin.y;
7167
7168     infoPtr->bAutoarrange = FALSE;
7169
7170     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7171 }
7172
7173 /***
7174  * DESCRIPTION:
7175  * Sets the state of one or many items.
7176  *
7177  * PARAMETER(S):
7178  * [I] infoPtr : valid pointer to the listview structure
7179  * [I] nItem : item index
7180  * [I] lpLVItem : item or subitem info
7181  *
7182  * RETURN:
7183  *   SUCCESS : TRUE
7184  *   FAILURE : FALSE
7185  */
7186 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7187 {
7188     BOOL bResult = TRUE;
7189     LVITEMW lvItem;
7190
7191     lvItem.iItem = nItem;
7192     lvItem.iSubItem = 0;
7193     lvItem.mask = LVIF_STATE;
7194     lvItem.state = lpLVItem->state;
7195     lvItem.stateMask = lpLVItem->stateMask;
7196     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7197
7198     if (nItem == -1)
7199     {
7200         /* apply to all items */
7201         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7202             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7203     }
7204     else
7205         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7206
7207     /*
7208      * Update selection mark
7209      *
7210      * Investigation on windows 2k showed that selection mark was updated
7211      * whenever a new selection was made, but if the selected item was
7212      * unselected it was not updated.
7213      *
7214      * we are probably still not 100% accurate, but this at least sets the
7215      * proper selection mark when it is needed
7216      */
7217
7218     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7219         ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7220     {
7221         int i;
7222         infoPtr->nSelectionMark = -1;
7223         for (i = 0; i < infoPtr->nItemCount; i++)
7224         {
7225             if (infoPtr->uCallbackMask & LVIS_SELECTED)
7226             {
7227                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7228                 {
7229                     infoPtr->nSelectionMark = i;
7230                     break;
7231                 }
7232             }
7233             else if (ranges_contain(infoPtr->selectionRanges, i))
7234             {
7235                 infoPtr->nSelectionMark = i;
7236                 break;
7237             }
7238         }
7239     }
7240
7241     return bResult;
7242 }
7243
7244 /***
7245  * DESCRIPTION:
7246  * Sets the text of an item or subitem.
7247  *
7248  * PARAMETER(S):
7249  * [I] hwnd : window handle
7250  * [I] nItem : item index
7251  * [I] lpLVItem : item or subitem info
7252  * [I] isW : TRUE if input is Unicode
7253  *
7254  * RETURN:
7255  *   SUCCESS : TRUE
7256  *   FAILURE : FALSE
7257  */
7258 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7259 {
7260     LVITEMW lvItem;
7261
7262     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7263     
7264     lvItem.iItem = nItem;
7265     lvItem.iSubItem = lpLVItem->iSubItem;
7266     lvItem.mask = LVIF_TEXT;
7267     lvItem.pszText = lpLVItem->pszText;
7268     lvItem.cchTextMax = lpLVItem->cchTextMax;
7269     
7270     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7271
7272     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
7273 }
7274
7275 /***
7276  * DESCRIPTION:
7277  * Set item index that marks the start of a multiple selection.
7278  *
7279  * PARAMETER(S):
7280  * [I] infoPtr : valid pointer to the listview structure
7281  * [I] nIndex : index
7282  *
7283  * RETURN:
7284  * Index number or -1 if there is no selection mark.
7285  */
7286 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7287 {
7288   INT nOldIndex = infoPtr->nSelectionMark;
7289
7290   TRACE("(nIndex=%d)\n", nIndex);
7291
7292   infoPtr->nSelectionMark = nIndex;
7293
7294   return nOldIndex;
7295 }
7296
7297 /***
7298  * DESCRIPTION:
7299  * Sets the text background color.
7300  *
7301  * PARAMETER(S):
7302  * [I] infoPtr : valid pointer to the listview structure
7303  * [I] clrTextBk : text background color
7304  *
7305  * RETURN:
7306  *   SUCCESS : TRUE
7307  *   FAILURE : FALSE
7308  */
7309 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7310 {
7311     TRACE("(clrTextBk=%lx)\n", clrTextBk);
7312
7313     if (infoPtr->clrTextBk != clrTextBk)
7314     {
7315         infoPtr->clrTextBk = clrTextBk;
7316         LISTVIEW_InvalidateList(infoPtr);
7317     }
7318     
7319   return TRUE;
7320 }
7321
7322 /***
7323  * DESCRIPTION:
7324  * Sets the text foreground color.
7325  *
7326  * PARAMETER(S):
7327  * [I] infoPtr : valid pointer to the listview structure
7328  * [I] clrText : text color
7329  *
7330  * RETURN:
7331  *   SUCCESS : TRUE
7332  *   FAILURE : FALSE
7333  */
7334 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7335 {
7336     TRACE("(clrText=%lx)\n", clrText);
7337
7338     if (infoPtr->clrText != clrText)
7339     {
7340         infoPtr->clrText = clrText;
7341         LISTVIEW_InvalidateList(infoPtr);
7342     }
7343
7344     return TRUE;
7345 }
7346
7347 /***
7348  * DESCRIPTION:
7349  * Determines which listview item is located at the specified position.
7350  *
7351  * PARAMETER(S):
7352  * [I] infoPtr        : valid pointer to the listview structure
7353  * [I] hwndNewToolTip : handle to new ToolTip
7354  *
7355  * RETURN:
7356  *   old tool tip
7357  */
7358 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7359 {
7360   HWND hwndOldToolTip = infoPtr->hwndToolTip;
7361   infoPtr->hwndToolTip = hwndNewToolTip;
7362   return hwndOldToolTip;
7363 }
7364
7365 /* LISTVIEW_SetUnicodeFormat */
7366 /* LISTVIEW_SetWorkAreas */
7367
7368 /***
7369  * DESCRIPTION:
7370  * Callback internally used by LISTVIEW_SortItems()
7371  *
7372  * PARAMETER(S):
7373  * [I] first : pointer to first ITEM_INFO to compare
7374  * [I] second : pointer to second ITEM_INFO to compare
7375  * [I] lParam : HWND of control
7376  *
7377  * RETURN:
7378  *   if first comes before second : negative
7379  *   if first comes after second : positive
7380  *   if first and second are equivalent : zero
7381  */
7382 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7383 {
7384   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7385   ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7386   ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7387
7388   /* Forward the call to the client defined callback */
7389   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7390 }
7391
7392 /***
7393  * DESCRIPTION:
7394  * Sorts the listview items.
7395  *
7396  * PARAMETER(S):
7397  * [I] infoPtr : valid pointer to the listview structure
7398  * [I] pfnCompare : application-defined value
7399  * [I] lParamSort : pointer to comparision callback
7400  *
7401  * RETURN:
7402  *   SUCCESS : TRUE
7403  *   FAILURE : FALSE
7404  */
7405 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7406 {
7407     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7408     HDPA hdpaSubItems;
7409     ITEM_INFO *lpItem;
7410     LPVOID selectionMarkItem;
7411     LVITEMW item;
7412     int i;
7413
7414     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7415
7416     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7417
7418     if (!pfnCompare) return FALSE;
7419     if (!infoPtr->hdpaItems) return FALSE;
7420
7421     /* if there are 0 or 1 items, there is no need to sort */
7422     if (infoPtr->nItemCount < 2) return TRUE;
7423
7424     if (infoPtr->nFocusedItem >= 0)
7425     {
7426         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7427         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7428         if (lpItem) lpItem->state |= LVIS_FOCUSED;
7429     }
7430     /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7431     /*        clear the lpItem->state for non-selected ones */
7432     /*        remove the selection ranges */
7433     
7434     infoPtr->pfnCompare = pfnCompare;
7435     infoPtr->lParamSort = lParamSort;
7436     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7437
7438     /* Adjust selections and indices so that they are the way they should
7439      * be after the sort (otherwise, the list items move around, but
7440      * whatever is at the item's previous original position will be
7441      * selected instead)
7442      */
7443     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7444     for (i=0; i < infoPtr->nItemCount; i++)
7445     {
7446         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7447         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7448
7449         if (lpItem->state & LVIS_SELECTED)
7450         {
7451             item.state = LVIS_SELECTED;
7452             item.stateMask = LVIS_SELECTED;
7453             LISTVIEW_SetItemState(infoPtr, i, &item);
7454         }
7455         if (lpItem->state & LVIS_FOCUSED)
7456         {
7457             infoPtr->nFocusedItem = i;
7458             lpItem->state &= ~LVIS_FOCUSED;
7459         }
7460     }
7461     if (selectionMarkItem != NULL)
7462         infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7463     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7464
7465     /* refresh the display */
7466     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7467         LISTVIEW_InvalidateList(infoPtr);
7468
7469     return TRUE;
7470 }
7471
7472 /***
7473  * DESCRIPTION:
7474  * Update theme handle after a theme change.
7475  *
7476  * PARAMETER(S):
7477  * [I] infoPtr : valid pointer to the listview structure
7478  *
7479  * RETURN:
7480  *   SUCCESS : 0
7481  *   FAILURE : something else
7482  */
7483 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7484 {
7485     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7486     CloseThemeData(theme);
7487     OpenThemeData(infoPtr->hwndSelf, themeClass);
7488     return 0;
7489 }
7490
7491 /***
7492  * DESCRIPTION:
7493  * Updates an items or rearranges the listview control.
7494  *
7495  * PARAMETER(S):
7496  * [I] infoPtr : valid pointer to the listview structure
7497  * [I] nItem : item index
7498  *
7499  * RETURN:
7500  *   SUCCESS : TRUE
7501  *   FAILURE : FALSE
7502  */
7503 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7504 {
7505     TRACE("(nItem=%d)\n", nItem);
7506
7507     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7508
7509     /* rearrange with default alignment style */
7510     if (is_autoarrange(infoPtr))
7511         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7512     else
7513         LISTVIEW_InvalidateItem(infoPtr, nItem);
7514
7515     return TRUE;
7516 }
7517
7518         
7519 /***
7520  * DESCRIPTION:
7521  * Creates the listview control.
7522  *
7523  * PARAMETER(S):
7524  * [I] hwnd : window handle
7525  * [I] lpcs : the create parameters
7526  *
7527  * RETURN:
7528  *   Success: 0
7529  *   Failure: -1
7530  */
7531 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7532 {
7533   LISTVIEW_INFO *infoPtr;
7534   UINT uView = lpcs->style & LVS_TYPEMASK;
7535   LOGFONTW logFont;
7536
7537   TRACE("(lpcs=%p)\n", lpcs);
7538
7539   /* initialize info pointer */
7540   infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7541   if (!infoPtr) return -1;
7542
7543   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7544
7545   infoPtr->hwndSelf = hwnd;
7546   infoPtr->dwStyle = lpcs->style;
7547   /* determine the type of structures to use */
7548   infoPtr->hwndNotify = lpcs->hwndParent;
7549   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7550                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7551
7552   /* initialize color information  */
7553   infoPtr->clrBk = CLR_NONE;
7554   infoPtr->clrText = comctl32_color.clrWindowText;
7555   infoPtr->clrTextBk = CLR_DEFAULT;
7556   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7557
7558   /* set default values */
7559   infoPtr->nFocusedItem = -1;
7560   infoPtr->nSelectionMark = -1;
7561   infoPtr->nHotItem = -1;
7562   infoPtr->bRedraw = TRUE;
7563   infoPtr->bNoItemMetrics = TRUE;
7564   infoPtr->bDoChangeNotify = TRUE;
7565   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7566   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7567   infoPtr->nEditLabelItem = -1;
7568   infoPtr->dwHoverTime = -1; /* default system hover time */
7569   infoPtr->nMeasureItemHeight = 0;
7570
7571   /* get default font (icon title) */
7572   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7573   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7574   infoPtr->hFont = infoPtr->hDefaultFont;
7575   LISTVIEW_SaveTextMetrics(infoPtr);
7576
7577   /* create header */
7578   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7579     WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7580     0, 0, 0, 0, hwnd, NULL,
7581     lpcs->hInstance, NULL);
7582   if (!infoPtr->hwndHeader) goto fail;
7583
7584   /* set header unicode format */
7585   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7586
7587   /* set header font */
7588   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7589
7590   /* allocate memory for the data structure */
7591   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7592   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7593   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
7594   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
7595   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7596
7597   /* initialize the icon sizes */
7598   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7599   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7600
7601   /* init item size to avoid division by 0 */
7602   LISTVIEW_UpdateItemSize (infoPtr);
7603   
7604   if (uView == LVS_REPORT)
7605   {
7606     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7607     {
7608       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7609     }
7610     else
7611     {
7612       /* set HDS_HIDDEN flag to hide the header bar */
7613       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7614                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7615     }
7616   }
7617
7618   OpenThemeData(hwnd, themeClass);
7619
7620   return 0;
7621
7622 fail:
7623     DestroyWindow(infoPtr->hwndHeader);
7624     ranges_destroy(infoPtr->selectionRanges);
7625     DPA_Destroy(infoPtr->hdpaItems);
7626     DPA_Destroy(infoPtr->hdpaPosX);
7627     DPA_Destroy(infoPtr->hdpaPosY);
7628     DPA_Destroy(infoPtr->hdpaColumns);
7629     Free(infoPtr);
7630     return -1;
7631 }
7632
7633 /***
7634  * DESCRIPTION:
7635  * Destroys the listview control.
7636  *
7637  * PARAMETER(S):
7638  * [I] infoPtr : valid pointer to the listview structure
7639  *
7640  * RETURN:
7641  *   Success: 0
7642  *   Failure: -1
7643  */
7644 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7645 {
7646     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7647     CloseThemeData(theme);
7648     return 0;
7649 }
7650
7651 /***
7652  * DESCRIPTION:
7653  * Enables the listview control.
7654  *
7655  * PARAMETER(S):
7656  * [I] infoPtr : valid pointer to the listview structure
7657  * [I] bEnable : specifies whether to enable or disable the window
7658  *
7659  * RETURN:
7660  *   SUCCESS : TRUE
7661  *   FAILURE : FALSE
7662  */
7663 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7664 {
7665     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7666         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7667     return TRUE;
7668 }
7669
7670 /***
7671  * DESCRIPTION:
7672  * Erases the background of the listview control.
7673  *
7674  * PARAMETER(S):
7675  * [I] infoPtr : valid pointer to the listview structure
7676  * [I] hdc : device context handle
7677  *
7678  * RETURN:
7679  *   SUCCESS : TRUE
7680  *   FAILURE : FALSE
7681  */
7682 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7683 {
7684     RECT rc;
7685
7686     TRACE("(hdc=%p)\n", hdc);
7687
7688     if (!GetClipBox(hdc, &rc)) return FALSE;
7689
7690     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7691 }
7692         
7693
7694 /***
7695  * DESCRIPTION:
7696  * Helper function for LISTVIEW_[HV]Scroll *only*.
7697  * Performs vertical/horizontal scrolling by a give amount.
7698  *
7699  * PARAMETER(S):
7700  * [I] infoPtr : valid pointer to the listview structure
7701  * [I] dx : amount of horizontal scroll
7702  * [I] dy : amount of vertical scroll
7703  */
7704 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7705 {
7706     /* now we can scroll the list */
7707     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
7708                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7709     /* if we have focus, adjust rect */
7710     OffsetRect(&infoPtr->rcFocus, dx, dy);
7711     UpdateWindow(infoPtr->hwndSelf);
7712 }
7713
7714 /***
7715  * DESCRIPTION:
7716  * Performs vertical scrolling.
7717  *
7718  * PARAMETER(S):
7719  * [I] infoPtr : valid pointer to the listview structure
7720  * [I] nScrollCode : scroll code
7721  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7722  * [I] hScrollWnd  : scrollbar control window handle
7723  *
7724  * RETURN:
7725  * Zero
7726  *
7727  * NOTES:
7728  *   SB_LINEUP/SB_LINEDOWN:
7729  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
7730  *        for LVS_REPORT is 1 line
7731  *        for LVS_LIST cannot occur
7732  *
7733  */
7734 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
7735                                 INT nScrollDiff, HWND hScrollWnd)
7736 {
7737     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7738     INT nOldScrollPos, nNewScrollPos;
7739     SCROLLINFO scrollInfo;
7740     BOOL is_an_icon;
7741
7742     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7743         debugscrollcode(nScrollCode), nScrollDiff);
7744
7745     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7746
7747     scrollInfo.cbSize = sizeof(SCROLLINFO);
7748     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7749
7750     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7751
7752     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7753
7754     nOldScrollPos = scrollInfo.nPos;
7755     switch (nScrollCode)
7756     {
7757     case SB_INTERNAL:
7758         break;
7759
7760     case SB_LINEUP:
7761         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7762         break;
7763
7764     case SB_LINEDOWN:
7765         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7766         break;
7767
7768     case SB_PAGEUP:
7769         nScrollDiff = -scrollInfo.nPage;
7770         break;
7771
7772     case SB_PAGEDOWN:
7773         nScrollDiff = scrollInfo.nPage;
7774         break;
7775
7776     case SB_THUMBPOSITION:
7777     case SB_THUMBTRACK:
7778         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7779         break;
7780
7781     default:
7782         nScrollDiff = 0;
7783     }
7784
7785     /* quit right away if pos isn't changing */
7786     if (nScrollDiff == 0) return 0;
7787     
7788     /* calculate new position, and handle overflows */
7789     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7790     if (nScrollDiff > 0) {
7791         if (nNewScrollPos < nOldScrollPos ||
7792             nNewScrollPos > scrollInfo.nMax)
7793             nNewScrollPos = scrollInfo.nMax;
7794     } else {
7795         if (nNewScrollPos > nOldScrollPos ||
7796             nNewScrollPos < scrollInfo.nMin)
7797             nNewScrollPos = scrollInfo.nMin;
7798     }
7799
7800     /* set the new position, and reread in case it changed */
7801     scrollInfo.fMask = SIF_POS;
7802     scrollInfo.nPos = nNewScrollPos;
7803     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7804     
7805     /* carry on only if it really changed */
7806     if (nNewScrollPos == nOldScrollPos) return 0;
7807     
7808     /* now adjust to client coordinates */
7809     nScrollDiff = nOldScrollPos - nNewScrollPos;
7810     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7811    
7812     /* and scroll the window */ 
7813     scroll_list(infoPtr, 0, nScrollDiff);
7814
7815     return 0;
7816 }
7817
7818 /***
7819  * DESCRIPTION:
7820  * Performs horizontal scrolling.
7821  *
7822  * PARAMETER(S):
7823  * [I] infoPtr : valid pointer to the listview structure
7824  * [I] nScrollCode : scroll code
7825  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7826  * [I] hScrollWnd  : scrollbar control window handle
7827  *
7828  * RETURN:
7829  * Zero
7830  *
7831  * NOTES:
7832  *   SB_LINELEFT/SB_LINERIGHT:
7833  *        for LVS_ICON, LVS_SMALLICON  1 pixel
7834  *        for LVS_REPORT is 1 pixel
7835  *        for LVS_LIST  is 1 column --> which is a 1 because the
7836  *                                      scroll is based on columns not pixels
7837  *
7838  */
7839 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7840                                 INT nScrollDiff, HWND hScrollWnd)
7841 {
7842     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7843     INT nOldScrollPos, nNewScrollPos;
7844     SCROLLINFO scrollInfo;
7845
7846     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7847         debugscrollcode(nScrollCode), nScrollDiff);
7848
7849     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7850
7851     scrollInfo.cbSize = sizeof(SCROLLINFO);
7852     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7853
7854     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7855
7856     nOldScrollPos = scrollInfo.nPos;
7857
7858     switch (nScrollCode)
7859     {
7860     case SB_INTERNAL:
7861         break;
7862
7863     case SB_LINELEFT:
7864         nScrollDiff = -1;
7865         break;
7866
7867     case SB_LINERIGHT:
7868         nScrollDiff = 1;
7869         break;
7870
7871     case SB_PAGELEFT:
7872         nScrollDiff = -scrollInfo.nPage;
7873         break;
7874
7875     case SB_PAGERIGHT:
7876         nScrollDiff = scrollInfo.nPage;
7877         break;
7878
7879     case SB_THUMBPOSITION:
7880     case SB_THUMBTRACK:
7881         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7882         break;
7883
7884     default:
7885         nScrollDiff = 0;
7886     }
7887
7888     /* quit right away if pos isn't changing */
7889     if (nScrollDiff == 0) return 0;
7890     
7891     /* calculate new position, and handle overflows */
7892     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7893     if (nScrollDiff > 0) {
7894         if (nNewScrollPos < nOldScrollPos ||
7895             nNewScrollPos > scrollInfo.nMax)
7896             nNewScrollPos = scrollInfo.nMax;
7897     } else {
7898         if (nNewScrollPos > nOldScrollPos ||
7899             nNewScrollPos < scrollInfo.nMin)
7900             nNewScrollPos = scrollInfo.nMin;
7901     }
7902
7903     /* set the new position, and reread in case it changed */
7904     scrollInfo.fMask = SIF_POS;
7905     scrollInfo.nPos = nNewScrollPos;
7906     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7907     
7908     /* carry on only if it really changed */
7909     if (nNewScrollPos == nOldScrollPos) return 0;
7910     
7911     if(uView == LVS_REPORT)
7912         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7913       
7914     /* now adjust to client coordinates */
7915     nScrollDiff = nOldScrollPos - nNewScrollPos;
7916     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7917    
7918     /* and scroll the window */
7919     scroll_list(infoPtr, nScrollDiff, 0);
7920
7921   return 0;
7922 }
7923
7924 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7925 {
7926     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7927     INT gcWheelDelta = 0;
7928     INT pulScrollLines = 3;
7929     SCROLLINFO scrollInfo;
7930
7931     TRACE("(wheelDelta=%d)\n", wheelDelta);
7932
7933     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7934     gcWheelDelta -= wheelDelta;
7935
7936     scrollInfo.cbSize = sizeof(SCROLLINFO);
7937     scrollInfo.fMask = SIF_POS;
7938
7939     switch(uView)
7940     {
7941     case LVS_ICON:
7942     case LVS_SMALLICON:
7943        /*
7944         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7945         *  should be fixed in the future.
7946         */
7947         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7948                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7949         break;
7950
7951     case LVS_REPORT:
7952         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7953         {
7954             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7955             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7956             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7957         }
7958         break;
7959
7960     case LVS_LIST:
7961         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7962         break;
7963     }
7964     return 0;
7965 }
7966
7967 /***
7968  * DESCRIPTION:
7969  * ???
7970  *
7971  * PARAMETER(S):
7972  * [I] infoPtr : valid pointer to the listview structure
7973  * [I] nVirtualKey : virtual key
7974  * [I] lKeyData : key data
7975  *
7976  * RETURN:
7977  * Zero
7978  */
7979 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7980 {
7981   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
7982   HWND hwndSelf = infoPtr->hwndSelf;
7983   INT nItem = -1;
7984   NMLVKEYDOWN nmKeyDown;
7985
7986   TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7987
7988   /* send LVN_KEYDOWN notification */
7989   nmKeyDown.wVKey = nVirtualKey;
7990   nmKeyDown.flags = 0;
7991   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7992   if (!IsWindow(hwndSelf))
7993     return 0;
7994
7995   switch (nVirtualKey)
7996   {
7997   case VK_SPACE:
7998     nItem = infoPtr->nFocusedItem;
7999     break;
8000
8001   case VK_RETURN:
8002     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8003     {
8004         if (!notify(infoPtr, NM_RETURN)) return 0;
8005         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8006     }
8007     break;
8008
8009   case VK_HOME:
8010     if (infoPtr->nItemCount > 0)
8011       nItem = 0;
8012     break;
8013
8014   case VK_END:
8015     if (infoPtr->nItemCount > 0)
8016       nItem = infoPtr->nItemCount - 1;
8017     break;
8018
8019   case VK_LEFT:
8020     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8021     break;
8022
8023   case VK_UP:
8024     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8025     break;
8026
8027   case VK_RIGHT:
8028     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8029     break;
8030
8031   case VK_DOWN:
8032     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8033     break;
8034
8035   case VK_PRIOR:
8036     if (uView == LVS_REPORT)
8037     {
8038       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8039       if (infoPtr->nFocusedItem == topidx)
8040         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8041       else
8042         nItem = topidx;
8043     }
8044     else
8045       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8046                                     * LISTVIEW_GetCountPerRow(infoPtr);
8047     if(nItem < 0) nItem = 0;
8048     break;
8049
8050   case VK_NEXT:
8051     if (uView == LVS_REPORT)
8052     {
8053       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8054       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8055       if (infoPtr->nFocusedItem == topidx + cnt - 1)
8056         nItem = infoPtr->nFocusedItem + cnt - 1;
8057       else
8058         nItem = topidx + cnt - 1;
8059     }
8060     else
8061       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8062                                     * LISTVIEW_GetCountPerRow(infoPtr);
8063     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8064     break;
8065   }
8066
8067   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8068       LISTVIEW_KeySelection(infoPtr, nItem);
8069
8070   return 0;
8071 }
8072
8073 /***
8074  * DESCRIPTION:
8075  * Kills the focus.
8076  *
8077  * PARAMETER(S):
8078  * [I] infoPtr : valid pointer to the listview structure
8079  *
8080  * RETURN:
8081  * Zero
8082  */
8083 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8084 {
8085     TRACE("()\n");
8086
8087     /* if we did not have the focus, there's nothing to do */
8088     if (!infoPtr->bFocus) return 0;
8089    
8090     /* send NM_KILLFOCUS notification */
8091     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8092
8093     /* if we have a focus rectagle, get rid of it */
8094     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8095     
8096     /* set window focus flag */
8097     infoPtr->bFocus = FALSE;
8098
8099     /* invalidate the selected items before reseting focus flag */
8100     LISTVIEW_InvalidateSelectedItems(infoPtr);
8101     
8102     return 0;
8103 }
8104
8105 /***
8106  * DESCRIPTION:
8107  * Processes double click messages (left mouse button).
8108  *
8109  * PARAMETER(S):
8110  * [I] infoPtr : valid pointer to the listview structure
8111  * [I] wKey : key flag
8112  * [I] x,y : mouse coordinate
8113  *
8114  * RETURN:
8115  * Zero
8116  */
8117 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8118 {
8119     LVHITTESTINFO htInfo;
8120
8121     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8122
8123     /* send NM_RELEASEDCAPTURE notification */
8124     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8125
8126     htInfo.pt.x = x;
8127     htInfo.pt.y = y;
8128
8129     /* send NM_DBLCLK notification */
8130     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8131     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8132
8133     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8134     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8135
8136     return 0;
8137 }
8138
8139 /***
8140  * DESCRIPTION:
8141  * Processes mouse down messages (left mouse button).
8142  *
8143  * PARAMETERS:
8144  *   infoPtr  [I ] valid pointer to the listview structure
8145  *   wKey     [I ] key flag
8146  *   x,y      [I ] mouse coordinate
8147  *
8148  * RETURN:
8149  *   Zero
8150  */
8151 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8152 {
8153   LVHITTESTINFO lvHitTestInfo;
8154   static BOOL bGroupSelect = TRUE;
8155   POINT pt = { x, y };
8156   INT nItem;
8157
8158   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8159
8160   /* send NM_RELEASEDCAPTURE notification */
8161   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8162
8163   if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8164
8165   /* set left button down flag and record the click position */
8166   infoPtr->bLButtonDown = TRUE;
8167   infoPtr->ptClickPos = pt;
8168
8169   lvHitTestInfo.pt.x = x;
8170   lvHitTestInfo.pt.y = y;
8171
8172   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8173   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8174   infoPtr->nEditLabelItem = -1;
8175   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8176   {
8177     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8178     {
8179         DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8180         if(state == 1 || state == 2)
8181         {
8182             LVITEMW lvitem;
8183             state ^= 3;
8184             lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8185             lvitem.stateMask = LVIS_STATEIMAGEMASK;
8186             LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8187         }
8188         return 0;
8189     }
8190
8191     if (infoPtr->dwStyle & LVS_SINGLESEL)
8192     {
8193       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8194         infoPtr->nEditLabelItem = nItem;
8195       else
8196         LISTVIEW_SetSelection(infoPtr, nItem);
8197     }
8198     else
8199     {
8200       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8201       {
8202         if (bGroupSelect)
8203         {
8204           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8205           LISTVIEW_SetItemFocus(infoPtr, nItem);
8206           infoPtr->nSelectionMark = nItem;
8207         }
8208         else
8209         {
8210           LVITEMW item;
8211
8212           item.state = LVIS_SELECTED | LVIS_FOCUSED;
8213           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8214
8215           LISTVIEW_SetItemState(infoPtr,nItem,&item);
8216           infoPtr->nSelectionMark = nItem;
8217         }
8218       }
8219       else if (wKey & MK_CONTROL)
8220       {
8221         LVITEMW item;
8222
8223         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8224         
8225         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8226         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8227         LISTVIEW_SetItemState(infoPtr, nItem, &item);
8228         infoPtr->nSelectionMark = nItem;
8229       }
8230       else  if (wKey & MK_SHIFT)
8231       {
8232         LISTVIEW_SetGroupSelection(infoPtr, nItem);
8233       }
8234       else
8235       {
8236         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8237           infoPtr->nEditLabelItem = nItem;
8238
8239         /* set selection (clears other pre-existing selections) */
8240         LISTVIEW_SetSelection(infoPtr, nItem);
8241       }
8242     }
8243   }
8244   else
8245   {
8246     /* remove all selections */
8247     LISTVIEW_DeselectAll(infoPtr);
8248     ReleaseCapture();
8249   }
8250
8251   return 0;
8252 }
8253
8254 /***
8255  * DESCRIPTION:
8256  * Processes mouse up messages (left mouse button).
8257  *
8258  * PARAMETERS:
8259  *   infoPtr [I ] valid pointer to the listview structure
8260  *   wKey    [I ] key flag
8261  *   x,y     [I ] mouse coordinate
8262  *
8263  * RETURN:
8264  *   Zero
8265  */
8266 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8267 {
8268     LVHITTESTINFO lvHitTestInfo;
8269     
8270     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8271
8272     if (!infoPtr->bLButtonDown) return 0;
8273
8274     lvHitTestInfo.pt.x = x;
8275     lvHitTestInfo.pt.y = y;
8276
8277     /* send NM_CLICK notification */
8278     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8279     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8280
8281     /* set left button flag */
8282     infoPtr->bLButtonDown = FALSE;
8283
8284     /* if we clicked on a selected item, edit the label */
8285     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8286         LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8287
8288     return 0;
8289 }
8290
8291 /***
8292  * DESCRIPTION:
8293  * Destroys the listview control (called after WM_DESTROY).
8294  *
8295  * PARAMETER(S):
8296  * [I] infoPtr : valid pointer to the listview structure
8297  *
8298  * RETURN:
8299  * Zero
8300  */
8301 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8302 {
8303   TRACE("()\n");
8304
8305   /* delete all items */
8306   LISTVIEW_DeleteAllItems(infoPtr);
8307
8308   /* destroy data structure */
8309   DPA_Destroy(infoPtr->hdpaItems);
8310   DPA_Destroy(infoPtr->hdpaPosX);
8311   DPA_Destroy(infoPtr->hdpaPosY);
8312   DPA_Destroy(infoPtr->hdpaColumns);
8313   ranges_destroy(infoPtr->selectionRanges);
8314
8315   /* destroy image lists */
8316   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8317   {
8318       if (infoPtr->himlNormal)
8319           ImageList_Destroy(infoPtr->himlNormal);
8320       if (infoPtr->himlSmall)
8321           ImageList_Destroy(infoPtr->himlSmall);
8322       if (infoPtr->himlState)
8323           ImageList_Destroy(infoPtr->himlState);
8324   }
8325
8326   /* destroy font, bkgnd brush */
8327   infoPtr->hFont = 0;
8328   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8329   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8330
8331   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8332
8333   /* free listview info pointer*/
8334   Free(infoPtr);
8335
8336   return 0;
8337 }
8338
8339 /***
8340  * DESCRIPTION:
8341  * Handles notifications from header.
8342  *
8343  * PARAMETER(S):
8344  * [I] infoPtr : valid pointer to the listview structure
8345  * [I] nCtrlId : control identifier
8346  * [I] lpnmh : notification information
8347  *
8348  * RETURN:
8349  * Zero
8350  */
8351 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8352 {
8353     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8354     HWND hwndSelf = infoPtr->hwndSelf;
8355     
8356     TRACE("(lpnmh=%p)\n", lpnmh);
8357
8358     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8359     
8360     switch (lpnmh->hdr.code)
8361     {    
8362         case HDN_ITEMCHANGINGW:
8363         case HDN_ITEMCHANGINGA:
8364             return notify_forward_header(infoPtr, lpnmh);
8365         case HDN_ITEMCHANGEDW:
8366         case HDN_ITEMCHANGEDA:
8367             notify_forward_header(infoPtr, lpnmh);
8368             if (!IsWindow(hwndSelf))
8369                 break;
8370             /* Fall through */
8371         case HDN_TRACKW:
8372         case HDN_TRACKA:
8373         {
8374             COLUMN_INFO *lpColumnInfo;
8375             INT dx, cxy;
8376
8377             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8378             {
8379                 HDITEMW hdi;
8380     
8381                 hdi.mask = HDI_WIDTH;
8382                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8383                 cxy = hdi.cxy;
8384             }
8385             else
8386                 cxy = lpnmh->pitem->cxy;
8387             
8388             /* determine how much we change since the last know position */
8389             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8390             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8391             if (dx != 0)
8392             {
8393                 lpColumnInfo->rcHeader.right += dx;
8394                 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8395                 LISTVIEW_UpdateItemSize(infoPtr);
8396                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8397                 {
8398                     POINT ptOrigin;
8399                     RECT rcCol = lpColumnInfo->rcHeader;
8400                     
8401                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8402                     OffsetRect(&rcCol, ptOrigin.x, 0);
8403                     
8404                     rcCol.top = infoPtr->rcList.top;
8405                     rcCol.bottom = infoPtr->rcList.bottom;
8406
8407                     /* resizing left-aligned columns leaves most of the left side untouched */
8408                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8409                     {
8410                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8411                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8412                     }
8413                     
8414                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8415                 }
8416             }
8417         }
8418         break;
8419
8420         case HDN_ITEMCLICKW:
8421         case HDN_ITEMCLICKA:
8422         {
8423             /* Handle sorting by Header Column */
8424             NMLISTVIEW nmlv;
8425
8426             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8427             nmlv.iItem = -1;
8428             nmlv.iSubItem = lpnmh->iItem;
8429             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8430         }
8431         break;
8432
8433         case HDN_DIVIDERDBLCLICKW:
8434         case HDN_DIVIDERDBLCLICKA:
8435             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8436             break;
8437     }
8438
8439     return 0;
8440 }
8441
8442 /***
8443  * DESCRIPTION:
8444  * Paint non-client area of control.
8445  *
8446  * PARAMETER(S):
8447  * [I] infoPtr : valid pointer to the listview structureof the sender
8448  * [I] region : update region
8449  *
8450  * RETURN:
8451  *  TRUE  - frame was painted
8452  *  FALSE - call default window proc
8453  */
8454 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8455 {
8456     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8457     HDC dc;
8458     RECT r;
8459     HRGN cliprgn;
8460     int cxEdge = GetSystemMetrics (SM_CXEDGE),
8461         cyEdge = GetSystemMetrics (SM_CYEDGE);
8462
8463     if (!theme) return FALSE;
8464
8465     GetWindowRect(infoPtr->hwndSelf, &r);
8466
8467     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8468         r.right - cxEdge, r.bottom - cyEdge);
8469     if (region != (HRGN)1)
8470         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8471     OffsetRect(&r, -r.left, -r.top);
8472
8473     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8474     OffsetRect(&r, -r.left, -r.top);
8475
8476     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8477         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8478     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8479     ReleaseDC(infoPtr->hwndSelf, dc);
8480
8481     /* Call default proc to get the scrollbars etc. painted */
8482     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8483
8484     return TRUE;
8485 }
8486
8487 /***
8488  * DESCRIPTION:
8489  * Determines the type of structure to use.
8490  *
8491  * PARAMETER(S):
8492  * [I] infoPtr : valid pointer to the listview structureof the sender
8493  * [I] hwndFrom : listview window handle
8494  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8495  *
8496  * RETURN:
8497  * Zero
8498  */
8499 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8500 {
8501     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8502
8503     if (nCommand != NF_REQUERY) return 0;
8504     
8505     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8506     
8507     return 0;
8508 }
8509
8510 /***
8511  * DESCRIPTION:
8512  * Paints/Repaints the listview control.
8513  *
8514  * PARAMETER(S):
8515  * [I] infoPtr : valid pointer to the listview structure
8516  * [I] hdc : device context handle
8517  *
8518  * RETURN:
8519  * Zero
8520  */
8521 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8522 {
8523     TRACE("(hdc=%p)\n", hdc);
8524
8525     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8526     {
8527         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8528         
8529         infoPtr->bNoItemMetrics = FALSE;
8530         LISTVIEW_UpdateItemSize(infoPtr);
8531         if (uView == LVS_ICON || uView == LVS_SMALLICON)
8532             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8533         LISTVIEW_UpdateScroll(infoPtr);
8534     }
8535     if (hdc) 
8536         LISTVIEW_Refresh(infoPtr, hdc);
8537     else
8538     {
8539         PAINTSTRUCT ps;
8540
8541         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8542         if (!hdc) return 1;
8543         if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8544         LISTVIEW_Refresh(infoPtr, hdc);
8545         EndPaint(infoPtr->hwndSelf, &ps);
8546     }
8547
8548     return 0;
8549 }
8550
8551
8552 /***
8553  * DESCRIPTION:
8554  * Paints/Repaints the listview control.
8555  *
8556  * PARAMETER(S):
8557  * [I] infoPtr : valid pointer to the listview structure
8558  * [I] hdc : device context handle
8559  * [I] options : drawing options
8560  *
8561  * RETURN:
8562  * Zero
8563  */
8564 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8565 {
8566     FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8567
8568     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8569         return 0;
8570
8571     if (options & PRF_ERASEBKGND)
8572         LISTVIEW_EraseBkgnd(infoPtr, hdc);
8573
8574     if (options & PRF_CLIENT)
8575         LISTVIEW_Paint(infoPtr, hdc);
8576
8577     return 0;
8578 }
8579
8580
8581 /***
8582  * DESCRIPTION:
8583  * Processes double click messages (right mouse button).
8584  *
8585  * PARAMETER(S):
8586  * [I] infoPtr : valid pointer to the listview structure
8587  * [I] wKey : key flag
8588  * [I] x,y : mouse coordinate
8589  *
8590  * RETURN:
8591  * Zero
8592  */
8593 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8594 {
8595     LVHITTESTINFO lvHitTestInfo;
8596     
8597     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8598
8599     /* send NM_RELEASEDCAPTURE notification */
8600     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8601
8602     /* send NM_RDBLCLK notification */
8603     lvHitTestInfo.pt.x = x;
8604     lvHitTestInfo.pt.y = y;
8605     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8606     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8607
8608     return 0;
8609 }
8610
8611 /***
8612  * DESCRIPTION:
8613  * Processes mouse down messages (right mouse button).
8614  *
8615  * PARAMETER(S):
8616  * [I] infoPtr : valid pointer to the listview structure
8617  * [I] wKey : key flag
8618  * [I] x,y : mouse coordinate
8619  *
8620  * RETURN:
8621  * Zero
8622  */
8623 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8624 {
8625     LVHITTESTINFO lvHitTestInfo;
8626     INT nItem;
8627
8628     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8629
8630     /* send NM_RELEASEDCAPTURE notification */
8631     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8632
8633     /* make sure the listview control window has the focus */
8634     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8635
8636     /* set right button down flag */
8637     infoPtr->bRButtonDown = TRUE;
8638
8639     /* determine the index of the selected item */
8640     lvHitTestInfo.pt.x = x;
8641     lvHitTestInfo.pt.y = y;
8642     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8643   
8644     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8645     {
8646         LISTVIEW_SetItemFocus(infoPtr, nItem);
8647         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8648             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8649             LISTVIEW_SetSelection(infoPtr, nItem);
8650     }
8651     else
8652     {
8653         LISTVIEW_DeselectAll(infoPtr);
8654     }
8655
8656     return 0;
8657 }
8658
8659 /***
8660  * DESCRIPTION:
8661  * Processes mouse up messages (right mouse button).
8662  *
8663  * PARAMETER(S):
8664  * [I] infoPtr : valid pointer to the listview structure
8665  * [I] wKey : key flag
8666  * [I] x,y : mouse coordinate
8667  *
8668  * RETURN:
8669  * Zero
8670  */
8671 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8672 {
8673     LVHITTESTINFO lvHitTestInfo;
8674     POINT pt;
8675
8676     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8677
8678     if (!infoPtr->bRButtonDown) return 0;
8679  
8680     /* set button flag */
8681     infoPtr->bRButtonDown = FALSE;
8682
8683     /* Send NM_RClICK notification */
8684     lvHitTestInfo.pt.x = x;
8685     lvHitTestInfo.pt.y = y;
8686     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8687     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8688
8689     /* Change to screen coordinate for WM_CONTEXTMENU */
8690     pt = lvHitTestInfo.pt;
8691     ClientToScreen(infoPtr->hwndSelf, &pt);
8692
8693     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8694     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8695                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8696
8697     return 0;
8698 }
8699
8700
8701 /***
8702  * DESCRIPTION:
8703  * Sets the cursor.
8704  *
8705  * PARAMETER(S):
8706  * [I] infoPtr : valid pointer to the listview structure
8707  * [I] hwnd : window handle of window containing the cursor
8708  * [I] nHittest : hit-test code
8709  * [I] wMouseMsg : ideintifier of the mouse message
8710  *
8711  * RETURN:
8712  * TRUE if cursor is set
8713  * FALSE otherwise
8714  */
8715 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8716 {
8717     LVHITTESTINFO lvHitTestInfo;
8718
8719     if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8720
8721     if(!infoPtr->hHotCursor)  return FALSE;
8722
8723     GetCursorPos(&lvHitTestInfo.pt);
8724     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8725
8726     SetCursor(infoPtr->hHotCursor);
8727
8728     return TRUE;
8729 }
8730
8731 /***
8732  * DESCRIPTION:
8733  * Sets the focus.
8734  *
8735  * PARAMETER(S):
8736  * [I] infoPtr : valid pointer to the listview structure
8737  * [I] hwndLoseFocus : handle of previously focused window
8738  *
8739  * RETURN:
8740  * Zero
8741  */
8742 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8743 {
8744     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8745
8746     /* if we have the focus already, there's nothing to do */
8747     if (infoPtr->bFocus) return 0;
8748    
8749     /* send NM_SETFOCUS notification */
8750     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8751
8752     /* set window focus flag */
8753     infoPtr->bFocus = TRUE;
8754
8755     /* put the focus rect back on */
8756     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8757
8758     /* redraw all visible selected items */
8759     LISTVIEW_InvalidateSelectedItems(infoPtr);
8760
8761     return 0;
8762 }
8763
8764 /***
8765  * DESCRIPTION:
8766  * Sets the font.
8767  *
8768  * PARAMETER(S):
8769  * [I] infoPtr : valid pointer to the listview structure
8770  * [I] fRedraw : font handle
8771  * [I] fRedraw : redraw flag
8772  *
8773  * RETURN:
8774  * Zero
8775  */
8776 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8777 {
8778     HFONT oldFont = infoPtr->hFont;
8779
8780     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8781
8782     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8783     if (infoPtr->hFont == oldFont) return 0;
8784     
8785     LISTVIEW_SaveTextMetrics(infoPtr);
8786
8787     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8788         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8789
8790     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8791
8792     return 0;
8793 }
8794
8795 /***
8796  * DESCRIPTION:
8797  * Message handling for WM_SETREDRAW.
8798  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8799  *
8800  * PARAMETER(S):
8801  * [I] infoPtr : valid pointer to the listview structure
8802  * [I] bRedraw: state of redraw flag
8803  *
8804  * RETURN:
8805  * DefWinProc return value
8806  */
8807 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8808 {
8809     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8810
8811     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8812     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8813
8814     infoPtr->bRedraw = bRedraw;
8815
8816     if(!bRedraw) return 0;
8817     
8818     if (is_autoarrange(infoPtr))
8819         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8820     LISTVIEW_UpdateScroll(infoPtr);
8821
8822     /* despite what the WM_SETREDRAW docs says, apps expect us
8823      * to invalidate the listview here... stupid! */
8824     LISTVIEW_InvalidateList(infoPtr);
8825
8826     return 0;
8827 }
8828
8829 /***
8830  * DESCRIPTION:
8831  * Resizes the listview control. This function processes WM_SIZE
8832  * messages.  At this time, the width and height are not used.
8833  *
8834  * PARAMETER(S):
8835  * [I] infoPtr : valid pointer to the listview structure
8836  * [I] Width : new width
8837  * [I] Height : new height
8838  *
8839  * RETURN:
8840  * Zero
8841  */
8842 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8843 {
8844     RECT rcOld = infoPtr->rcList;
8845
8846     TRACE("(width=%d, height=%d)\n", Width, Height);
8847
8848     LISTVIEW_UpdateSize(infoPtr);
8849     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8850   
8851     /* do not bother with display related stuff if we're not redrawing */ 
8852     if (!is_redrawing(infoPtr)) return 0;
8853     
8854     if (is_autoarrange(infoPtr)) 
8855         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8856
8857     LISTVIEW_UpdateScroll(infoPtr);
8858
8859     /* refresh all only for lists whose height changed significantly */
8860     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
8861         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8862         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8863         LISTVIEW_InvalidateList(infoPtr);
8864
8865   return 0;
8866 }
8867
8868 /***
8869  * DESCRIPTION:
8870  * Sets the size information.
8871  *
8872  * PARAMETER(S):
8873  * [I] infoPtr : valid pointer to the listview structure
8874  *
8875  * RETURN:
8876  *  None
8877  */
8878 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8879 {
8880     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8881
8882     TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
8883     
8884     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8885
8886     if (uView == LVS_LIST)
8887     {
8888         /* Apparently the "LIST" style is supposed to have the same
8889          * number of items in a column even if there is no scroll bar.
8890          * Since if a scroll bar already exists then the bottom is already
8891          * reduced, only reduce if the scroll bar does not currently exist.
8892          * The "2" is there to mimic the native control. I think it may be
8893          * related to either padding or edges.  (GLA 7/2002)
8894          */
8895         if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8896             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8897         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8898     }
8899     else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8900     {
8901         HDLAYOUT hl;
8902         WINDOWPOS wp;
8903
8904         hl.prc = &infoPtr->rcList;
8905         hl.pwpos = &wp;
8906         Header_Layout(infoPtr->hwndHeader, &hl);
8907
8908         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8909
8910         infoPtr->rcList.top = max(wp.cy, 0);
8911     }
8912
8913     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
8914 }
8915
8916 /***
8917  * DESCRIPTION:
8918  * Processes WM_STYLECHANGED messages.
8919  *
8920  * PARAMETER(S):
8921  * [I] infoPtr : valid pointer to the listview structure
8922  * [I] wStyleType : window style type (normal or extended)
8923  * [I] lpss : window style information
8924  *
8925  * RETURN:
8926  * Zero
8927  */
8928 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8929                                  const STYLESTRUCT *lpss)
8930 {
8931     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8932     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8933
8934     TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8935           wStyleType, lpss->styleOld, lpss->styleNew);
8936
8937     if (wStyleType != GWL_STYLE) return 0;
8938   
8939     /* FIXME: if LVS_NOSORTHEADER changed, update header */
8940     /*        what if LVS_OWNERDATA changed? */
8941     /*        or LVS_SINGLESEL */
8942     /*        or LVS_SORT{AS,DES}CENDING */
8943
8944     infoPtr->dwStyle = lpss->styleNew;
8945
8946     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8947         ((lpss->styleNew & WS_HSCROLL) == 0))
8948        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8949
8950     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8951         ((lpss->styleNew & WS_VSCROLL) == 0))
8952        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8953
8954     if (uNewView != uOldView)
8955     {
8956         SIZE oldIconSize = infoPtr->iconSize;
8957         HIMAGELIST himl;
8958     
8959         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8960         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8961
8962         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8963         SetRectEmpty(&infoPtr->rcFocus);
8964
8965         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8966         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8967     
8968         if (uNewView == LVS_ICON)
8969         {
8970             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8971             {
8972                 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8973                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8974                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8975             }
8976         }
8977         else if (uNewView == LVS_REPORT)
8978         {
8979             HDLAYOUT hl;
8980             WINDOWPOS wp;
8981
8982             hl.prc = &infoPtr->rcList;
8983             hl.pwpos = &wp;
8984             Header_Layout(infoPtr->hwndHeader, &hl);
8985             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8986         }
8987
8988         LISTVIEW_UpdateItemSize(infoPtr);
8989     }
8990
8991     if (uNewView == LVS_REPORT)
8992         ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8993      
8994     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8995          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8996          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8997
8998     /* update the size of the client area */
8999     LISTVIEW_UpdateSize(infoPtr);
9000
9001     /* add scrollbars if needed */
9002     LISTVIEW_UpdateScroll(infoPtr);
9003
9004     /* invalidate client area + erase background */
9005     LISTVIEW_InvalidateList(infoPtr);
9006
9007     return 0;
9008 }
9009
9010 /***
9011  * DESCRIPTION:
9012  * Window procedure of the listview control.
9013  *
9014  */
9015 static LRESULT WINAPI
9016 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9017 {
9018   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9019
9020   TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9021
9022   if (!infoPtr && (uMsg != WM_CREATE))
9023     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9024
9025   switch (uMsg)
9026   {
9027   case LVM_APPROXIMATEVIEWRECT:
9028     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9029                                         LOWORD(lParam), HIWORD(lParam));
9030   case LVM_ARRANGE:
9031     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9032
9033 /* case LVM_CANCELEDITLABEL: */
9034
9035   case LVM_CREATEDRAGIMAGE:
9036     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9037
9038   case LVM_DELETEALLITEMS:
9039     return LISTVIEW_DeleteAllItems(infoPtr);
9040
9041   case LVM_DELETECOLUMN:
9042     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9043
9044   case LVM_DELETEITEM:
9045     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9046
9047   case LVM_EDITLABELW:
9048     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9049
9050   case LVM_EDITLABELA:
9051     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9052
9053   /* case LVM_ENABLEGROUPVIEW: */
9054
9055   case LVM_ENSUREVISIBLE:
9056     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9057
9058   case LVM_FINDITEMW:
9059     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9060
9061   case LVM_FINDITEMA:
9062     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9063
9064   case LVM_GETBKCOLOR:
9065     return infoPtr->clrBk;
9066
9067   /* case LVM_GETBKIMAGE: */
9068
9069   case LVM_GETCALLBACKMASK:
9070     return infoPtr->uCallbackMask;
9071
9072   case LVM_GETCOLUMNA:
9073     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9074
9075   case LVM_GETCOLUMNW:
9076     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9077
9078   case LVM_GETCOLUMNORDERARRAY:
9079     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9080
9081   case LVM_GETCOLUMNWIDTH:
9082     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9083
9084   case LVM_GETCOUNTPERPAGE:
9085     return LISTVIEW_GetCountPerPage(infoPtr);
9086
9087   case LVM_GETEDITCONTROL:
9088     return (LRESULT)infoPtr->hwndEdit;
9089
9090   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9091     return infoPtr->dwLvExStyle;
9092
9093   /* case LVM_GETGROUPINFO: */
9094
9095   /* case LVM_GETGROUPMETRICS: */
9096
9097   case LVM_GETHEADER:
9098     return (LRESULT)infoPtr->hwndHeader;
9099
9100   case LVM_GETHOTCURSOR:
9101     return (LRESULT)infoPtr->hHotCursor;
9102
9103   case LVM_GETHOTITEM:
9104     return infoPtr->nHotItem;
9105
9106   case LVM_GETHOVERTIME:
9107     return infoPtr->dwHoverTime;
9108
9109   case LVM_GETIMAGELIST:
9110     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9111
9112   /* case LVM_GETINSERTMARK: */
9113
9114   /* case LVM_GETINSERTMARKCOLOR: */
9115
9116   /* case LVM_GETINSERTMARKRECT: */
9117
9118   case LVM_GETISEARCHSTRINGA:
9119   case LVM_GETISEARCHSTRINGW:
9120     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9121     return FALSE;
9122
9123   case LVM_GETITEMA:
9124     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9125
9126   case LVM_GETITEMW:
9127     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9128
9129   case LVM_GETITEMCOUNT:
9130     return infoPtr->nItemCount;
9131
9132   case LVM_GETITEMPOSITION:
9133     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9134
9135   case LVM_GETITEMRECT:
9136     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9137
9138   case LVM_GETITEMSPACING:
9139     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9140
9141   case LVM_GETITEMSTATE:
9142     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9143
9144   case LVM_GETITEMTEXTA:
9145     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9146
9147   case LVM_GETITEMTEXTW:
9148     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9149
9150   case LVM_GETNEXTITEM:
9151     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9152
9153   case LVM_GETNUMBEROFWORKAREAS:
9154     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9155     return 1;
9156
9157   case LVM_GETORIGIN:
9158     if (!lParam) return FALSE;
9159     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9160     return TRUE;
9161
9162   /* case LVM_GETOUTLINECOLOR: */
9163
9164   /* case LVM_GETSELECTEDCOLUMN: */
9165
9166   case LVM_GETSELECTEDCOUNT:
9167     return LISTVIEW_GetSelectedCount(infoPtr);
9168
9169   case LVM_GETSELECTIONMARK:
9170     return infoPtr->nSelectionMark;
9171
9172   case LVM_GETSTRINGWIDTHA:
9173     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9174
9175   case LVM_GETSTRINGWIDTHW:
9176     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9177
9178   case LVM_GETSUBITEMRECT:
9179     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9180
9181   case LVM_GETTEXTBKCOLOR:
9182     return infoPtr->clrTextBk;
9183
9184   case LVM_GETTEXTCOLOR:
9185     return infoPtr->clrText;
9186
9187   /* case LVM_GETTILEINFO: */
9188
9189   /* case LVM_GETTILEVIEWINFO: */
9190
9191   case LVM_GETTOOLTIPS:
9192     if( !infoPtr->hwndToolTip )
9193         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9194     return (LRESULT)infoPtr->hwndToolTip;
9195
9196   case LVM_GETTOPINDEX:
9197     return LISTVIEW_GetTopIndex(infoPtr);
9198
9199   /*case LVM_GETUNICODEFORMAT:
9200     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9201     return FALSE;*/
9202
9203   /* case LVM_GETVIEW: */
9204
9205   case LVM_GETVIEWRECT:
9206     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9207
9208   case LVM_GETWORKAREAS:
9209     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9210     return FALSE;
9211
9212   /* case LVM_HASGROUP: */
9213
9214   case LVM_HITTEST:
9215     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9216
9217   case LVM_INSERTCOLUMNA:
9218     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9219
9220   case LVM_INSERTCOLUMNW:
9221     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9222
9223   /* case LVM_INSERTGROUP: */
9224
9225   /* case LVM_INSERTGROUPSORTED: */
9226
9227   case LVM_INSERTITEMA:
9228     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9229
9230   case LVM_INSERTITEMW:
9231     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9232
9233   /* case LVM_INSERTMARKHITTEST: */
9234
9235   /* case LVM_ISGROUPVIEWENABLED: */
9236
9237   /* case LVM_MAPIDTOINDEX: */
9238
9239   /* case LVM_MAPINDEXTOID: */
9240
9241   /* case LVM_MOVEGROUP: */
9242
9243   /* case LVM_MOVEITEMTOGROUP: */
9244
9245   case LVM_REDRAWITEMS:
9246     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9247
9248   /* case LVM_REMOVEALLGROUPS: */
9249
9250   /* case LVM_REMOVEGROUP: */
9251
9252   case LVM_SCROLL:
9253     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9254
9255   case LVM_SETBKCOLOR:
9256     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9257
9258   /* case LVM_SETBKIMAGE: */
9259
9260   case LVM_SETCALLBACKMASK:
9261     infoPtr->uCallbackMask = (UINT)wParam;
9262     return TRUE;
9263
9264   case LVM_SETCOLUMNA:
9265     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9266
9267   case LVM_SETCOLUMNW:
9268     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9269
9270   case LVM_SETCOLUMNORDERARRAY:
9271     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9272
9273   case LVM_SETCOLUMNWIDTH:
9274     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9275
9276   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9277     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9278
9279   /* case LVM_SETGROUPINFO: */
9280
9281   /* case LVM_SETGROUPMETRICS: */
9282
9283   case LVM_SETHOTCURSOR:
9284     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9285
9286   case LVM_SETHOTITEM:
9287     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9288
9289   case LVM_SETHOVERTIME:
9290     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9291
9292   case LVM_SETICONSPACING:
9293     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9294
9295   case LVM_SETIMAGELIST:
9296     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9297
9298   /* case LVM_SETINFOTIP: */
9299
9300   /* case LVM_SETINSERTMARK: */
9301
9302   /* case LVM_SETINSERTMARKCOLOR: */
9303
9304   case LVM_SETITEMA:
9305     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9306
9307   case LVM_SETITEMW:
9308     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9309
9310   case LVM_SETITEMCOUNT:
9311     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9312
9313   case LVM_SETITEMPOSITION:
9314     {
9315         POINT pt;
9316         pt.x = (short)LOWORD(lParam);
9317         pt.y = (short)HIWORD(lParam);
9318         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9319     }
9320
9321   case LVM_SETITEMPOSITION32:
9322     if (lParam == 0) return FALSE;
9323     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9324
9325   case LVM_SETITEMSTATE:
9326     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9327
9328   case LVM_SETITEMTEXTA:
9329     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9330
9331   case LVM_SETITEMTEXTW:
9332     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9333
9334   /* case LVM_SETOUTLINECOLOR: */
9335
9336   /* case LVM_SETSELECTEDCOLUMN: */
9337
9338   case LVM_SETSELECTIONMARK:
9339     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9340
9341   case LVM_SETTEXTBKCOLOR:
9342     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9343
9344   case LVM_SETTEXTCOLOR:
9345     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9346
9347   /* case LVM_SETTILEINFO: */
9348
9349   /* case LVM_SETTILEVIEWINFO: */
9350
9351   /* case LVM_SETTILEWIDTH: */
9352
9353   case LVM_SETTOOLTIPS:
9354     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9355
9356   /* case LVM_SETUNICODEFORMAT: */
9357
9358   /* case LVM_SETVIEW: */
9359
9360   /* case LVM_SETWORKAREAS: */
9361
9362   /* case LVM_SORTGROUPS: */
9363
9364   case LVM_SORTITEMS:
9365     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9366
9367   /* LVM_SORTITEMSEX: */
9368
9369   case LVM_SUBITEMHITTEST:
9370     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9371
9372   case LVM_UPDATE:
9373     return LISTVIEW_Update(infoPtr, (INT)wParam);
9374
9375   case WM_CHAR:
9376     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9377
9378   case WM_COMMAND:
9379     return LISTVIEW_Command(infoPtr, wParam, lParam);
9380
9381   case WM_CREATE:
9382     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9383
9384   case WM_DESTROY:
9385     return LISTVIEW_Destroy(infoPtr);
9386
9387   case WM_ENABLE:
9388     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9389
9390   case WM_ERASEBKGND:
9391     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9392
9393   case WM_GETDLGCODE:
9394     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9395
9396   case WM_GETFONT:
9397     return (LRESULT)infoPtr->hFont;
9398
9399   case WM_HSCROLL:
9400     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9401
9402   case WM_KEYDOWN:
9403     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9404
9405   case WM_KILLFOCUS:
9406     return LISTVIEW_KillFocus(infoPtr);
9407
9408   case WM_LBUTTONDBLCLK:
9409     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9410
9411   case WM_LBUTTONDOWN:
9412     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9413
9414   case WM_LBUTTONUP:
9415     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9416
9417   case WM_MOUSEMOVE:
9418     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9419
9420   case WM_MOUSEHOVER:
9421     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9422
9423   case WM_NCDESTROY:
9424     return LISTVIEW_NCDestroy(infoPtr);
9425
9426   case WM_NCPAINT:
9427     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9428         return 0;
9429     goto fwd_msg;
9430
9431   case WM_NOTIFY:
9432     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9433         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9434     else return 0;
9435
9436   case WM_NOTIFYFORMAT:
9437     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9438
9439   case WM_PRINTCLIENT:
9440     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9441
9442   case WM_PAINT:
9443     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9444
9445   case WM_RBUTTONDBLCLK:
9446     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9447
9448   case WM_RBUTTONDOWN:
9449     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9450
9451   case WM_RBUTTONUP:
9452     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9453
9454   case WM_SETCURSOR:
9455     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9456       return TRUE;
9457     goto fwd_msg;
9458
9459   case WM_SETFOCUS:
9460     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9461
9462   case WM_SETFONT:
9463     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9464
9465   case WM_SETREDRAW:
9466     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9467
9468   case WM_SIZE:
9469     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9470
9471   case WM_STYLECHANGED:
9472     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9473
9474   case WM_SYSCOLORCHANGE:
9475     COMCTL32_RefreshSysColors();
9476     return 0;
9477
9478 /*      case WM_TIMER: */
9479   case WM_THEMECHANGED:
9480     return LISTVIEW_ThemeChanged(infoPtr);
9481
9482   case WM_VSCROLL:
9483     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9484
9485   case WM_MOUSEWHEEL:
9486       if (wParam & (MK_SHIFT | MK_CONTROL))
9487           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9488       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9489
9490   case WM_WINDOWPOSCHANGED:
9491       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
9492       {
9493       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9494           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9495                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9496
9497       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9498       {
9499           MEASUREITEMSTRUCT mis;
9500           mis.CtlType = ODT_LISTVIEW;
9501           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9502           mis.itemID = -1;
9503           mis.itemWidth = 0;
9504           mis.itemData = 0;
9505           mis.itemHeight= infoPtr->nItemHeight;
9506           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9507           if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9508               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9509       }
9510
9511           LISTVIEW_UpdateSize(infoPtr);
9512           LISTVIEW_UpdateScroll(infoPtr);
9513       }
9514       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9515
9516 /*      case WM_WININICHANGE: */
9517
9518   default:
9519     if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9520       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9521
9522   fwd_msg:
9523     /* call default window procedure */
9524     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9525   }
9526
9527 }
9528
9529 /***
9530  * DESCRIPTION:
9531  * Registers the window class.
9532  *
9533  * PARAMETER(S):
9534  * None
9535  *
9536  * RETURN:
9537  * None
9538  */
9539 void LISTVIEW_Register(void)
9540 {
9541     WNDCLASSW wndClass;
9542
9543     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9544     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9545     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9546     wndClass.cbClsExtra = 0;
9547     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9548     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9549     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9550     wndClass.lpszClassName = WC_LISTVIEWW;
9551     RegisterClassW(&wndClass);
9552 }
9553
9554 /***
9555  * DESCRIPTION:
9556  * Unregisters the window class.
9557  *
9558  * PARAMETER(S):
9559  * None
9560  *
9561  * RETURN:
9562  * None
9563  */
9564 void LISTVIEW_Unregister(void)
9565 {
9566     UnregisterClassW(WC_LISTVIEWW, NULL);
9567 }
9568
9569 /***
9570  * DESCRIPTION:
9571  * Handle any WM_COMMAND messages
9572  *
9573  * PARAMETER(S):
9574  * [I] infoPtr : valid pointer to the listview structure
9575  * [I] wParam : the first message parameter
9576  * [I] lParam : the second message parameter
9577  *
9578  * RETURN:
9579  *   Zero.
9580  */
9581 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9582 {
9583     switch (HIWORD(wParam))
9584     {
9585         case EN_UPDATE:
9586         {
9587             /*
9588              * Adjust the edit window size
9589              */
9590             WCHAR buffer[1024];
9591             HDC           hdc = GetDC(infoPtr->hwndEdit);
9592             HFONT         hFont, hOldFont = 0;
9593             RECT          rect;
9594             SIZE          sz;
9595             int           len;
9596
9597             if (!infoPtr->hwndEdit || !hdc) return 0;
9598             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9599             GetWindowRect(infoPtr->hwndEdit, &rect);
9600
9601             /* Select font to get the right dimension of the string */
9602             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9603             if(hFont != 0)
9604             {
9605                 hOldFont = SelectObject(hdc, hFont);
9606             }
9607
9608             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9609             {
9610                 TEXTMETRICW textMetric;
9611
9612                 /* Add Extra spacing for the next character */
9613                 GetTextMetricsW(hdc, &textMetric);
9614                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9615
9616                 SetWindowPos (
9617                     infoPtr->hwndEdit,
9618                     HWND_TOP,
9619                     0,
9620                     0,
9621                     sz.cx,
9622                     rect.bottom - rect.top,
9623                     SWP_DRAWFRAME|SWP_NOMOVE);
9624             }
9625             if(hFont != 0)
9626                 SelectObject(hdc, hOldFont);
9627
9628             ReleaseDC(infoPtr->hwndEdit, hdc);
9629
9630             break;
9631         }
9632
9633         default:
9634           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9635     }
9636
9637     return 0;
9638 }
9639
9640
9641 /***
9642  * DESCRIPTION:
9643  * Subclassed edit control windproc function
9644  *
9645  * PARAMETER(S):
9646  * [I] hwnd : the edit window handle
9647  * [I] uMsg : the message that is to be processed
9648  * [I] wParam : first message parameter
9649  * [I] lParam : second message parameter
9650  * [I] isW : TRUE if input is Unicode
9651  *
9652  * RETURN:
9653  *   Zero.
9654  */
9655 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9656 {
9657     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9658     BOOL cancel = FALSE;
9659
9660     TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9661           hwnd, uMsg, wParam, lParam, isW);
9662
9663     switch (uMsg)
9664     {
9665         case WM_GETDLGCODE:
9666           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9667
9668         case WM_KILLFOCUS:
9669             break;
9670
9671         case WM_DESTROY:
9672         {
9673             WNDPROC editProc = infoPtr->EditWndProc;
9674             infoPtr->EditWndProc = 0;
9675             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9676             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9677         }
9678
9679         case WM_KEYDOWN:
9680             if (VK_ESCAPE == (INT)wParam)
9681             {
9682                 cancel = TRUE;
9683                 break;
9684             }
9685             else if (VK_RETURN == (INT)wParam)
9686                 break;
9687
9688         default:
9689             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9690     }
9691
9692     /* kill the edit */
9693     if (infoPtr->hwndEdit)
9694     {
9695         LPWSTR buffer = NULL;
9696
9697         infoPtr->hwndEdit = 0;
9698         if (!cancel)
9699         {
9700             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9701
9702             if (len)
9703             {
9704                 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9705                 {
9706                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
9707                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9708                 }
9709             }
9710         }
9711         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9712
9713         if (buffer) Free(buffer);
9714
9715     }
9716
9717     SendMessageW(hwnd, WM_CLOSE, 0, 0);
9718     return 0;
9719 }
9720
9721 /***
9722  * DESCRIPTION:
9723  * Subclassed edit control Unicode windproc function
9724  *
9725  * PARAMETER(S):
9726  * [I] hwnd : the edit window handle
9727  * [I] uMsg : the message that is to be processed
9728  * [I] wParam : first message parameter
9729  * [I] lParam : second message parameter
9730  *
9731  * RETURN:
9732  */
9733 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9734 {
9735     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9736 }
9737
9738 /***
9739  * DESCRIPTION:
9740  * Subclassed edit control ANSI windproc function
9741  *
9742  * PARAMETER(S):
9743  * [I] hwnd : the edit window handle
9744  * [I] uMsg : the message that is to be processed
9745  * [I] wParam : first message parameter
9746  * [I] lParam : second message parameter
9747  *
9748  * RETURN:
9749  */
9750 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9751 {
9752     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9753 }
9754
9755 /***
9756  * DESCRIPTION:
9757  * Creates a subclassed edit cotrol
9758  *
9759  * PARAMETER(S):
9760  * [I] infoPtr : valid pointer to the listview structure
9761  * [I] text : initial text for the edit
9762  * [I] style : the window style
9763  * [I] isW : TRUE if input is Unicode
9764  *
9765  * RETURN:
9766  */
9767 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9768         INT x, INT y, INT width, INT height, BOOL isW)
9769 {
9770     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9771     HWND hedit;
9772     SIZE sz;
9773     HDC hdc;
9774     HDC hOldFont=0;
9775     TEXTMETRICW textMetric;
9776     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9777
9778     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9779
9780     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9781     hdc = GetDC(infoPtr->hwndSelf);
9782
9783     /* Select the font to get appropriate metric dimensions */
9784     if(infoPtr->hFont != 0)
9785         hOldFont = SelectObject(hdc, infoPtr->hFont);
9786
9787     /*Get String Length in pixels */
9788     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9789
9790     /*Add Extra spacing for the next character */
9791     GetTextMetricsW(hdc, &textMetric);
9792     sz.cx += (textMetric.tmMaxCharWidth * 2);
9793
9794     if(infoPtr->hFont != 0)
9795         SelectObject(hdc, hOldFont);
9796
9797     ReleaseDC(infoPtr->hwndSelf, hdc);
9798     if (isW)
9799         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9800     else
9801         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9802
9803     if (!hedit) return 0;
9804
9805     infoPtr->EditWndProc = (WNDPROC)
9806         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9807                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9808
9809     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9810
9811     return hedit;
9812 }