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