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