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