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