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