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