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