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