dmloader: complete rewrite and full implementation.
[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 believe this code to be complete, as per
30  * the specification mentioned above.
31  * If you discover missing features, or bugs, please note them below.
32  * 
33  * TODO:
34  *
35  * Features
36  *   -- Hot item handling, mouse hovering
37  *   -- Workareas support
38  *   -- Tilemode support
39  *   -- Groups support
40  *
41  * Bugs
42  *   -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43  *   -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44  *   -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED 
45  *   -- LVA_SNAPTOGRID not implemented
46  *   -- LISTVIEW_ApproximateViewRect partially implemented
47  *   -- LISTVIEW_[GS]etColumnOrderArray stubs
48  *   -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49  *   -- LISTVIEW_SetIconSpacing is incomplete
50  *   -- LISTVIEW_SortItems is broken
51  *   -- LISTVIEW_StyleChanged doesn't handle some changes too well
52  *
53  * Speedups
54  *   -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55  *      linear in the number of items in the list, and this is
56  *      unacceptable for large lists.
57  *   -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58  *      instead of inserting in the right spot
59  *   -- we should keep an ordered array of coordinates in iconic mode
60  *      this would allow to frame items (iterator_frameditems),
61  *      and find nearest item (LVFI_NEARESTXY) a lot more efficiently
62  *
63  * Flags
64  *   -- LVIF_COLUMNS
65  *   -- LVIF_GROUPID
66  *   -- LVIF_NORECOMPUTE
67  *
68  * States
69  *   -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
70  *   -- LVIS_CUT
71  *   -- LVIS_DROPHILITED
72  *   -- LVIS_OVERLAYMASK
73  *
74  * Styles
75  *   -- LVS_NOLABELWRAP
76  *   -- LVS_NOSCROLL (see Q137520)
77  *   -- LVS_SORTASCENDING, LVS_SORTDESCENDING
78  *
79  * Extended Styles
80  *   -- LVS_EX_BORDERSELECT
81  *   -- LVS_EX_FLATSB
82  *   -- LVS_EX_GRIDLINES
83  *   -- LVS_EX_HEADERDRAGDROP
84  *   -- LVS_EX_INFOTIP
85  *   -- LVS_EX_LABELTIP
86  *   -- LVS_EX_MULTIWORKAREAS
87  *   -- LVS_EX_ONECLICKACTIVATE
88  *   -- LVS_EX_REGIONAL
89  *   -- LVS_EX_SIMPLESELECT
90  *   -- LVS_EX_TRACKSELECT
91  *   -- LVS_EX_TWOCLICKACTIVATE
92  *   -- LVS_EX_UNDERLINECOLD
93  *   -- LVS_EX_UNDERLINEHOT
94  *   
95  * Notifications:
96  *   -- LVN_BEGINSCROLL, LVN_ENDSCROLL
97  *   -- LVN_GETINFOTIP
98  *   -- LVN_HOTTRACK
99  *   -- LVN_MARQUEEBEGIN
100  *   -- LVN_ODFINDITEM
101  *   -- LVN_ODSTATECHANGED
102  *   -- LVN_SETDISPINFO
103  *   -- NM_HOVER
104  *
105  * Messages:
106  *   -- LVM_CANCELEDITLABEL
107  *   -- LVM_ENABLEGROUPVIEW
108  *   -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
109  *   -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
110  *   -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
111  *   -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
112  *   -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
113  *   -- LVM_GETINSERTMARKRECT
114  *   -- LVM_GETNUMBEROFWORKAREAS
115  *   -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
116  *   -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
117  *   -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
118  *   -- LVM_GETTILEINFO, LVM_SETTILEINFO
119  *   -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
120  *   -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
121  *   -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
122  *   -- LVM_GETVIEW, LVM_SETVIEW
123  *   -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
124  *   -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
125  *   -- LVM_INSERTGROUPSORTED
126  *   -- LVM_INSERTMARKHITTEST
127  *   -- LVM_ISGROUPVIEWENABLED
128  *   -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
129  *   -- LVM_MOVEGROUP
130  *   -- LVM_MOVEITEMTOGROUP
131  *   -- LVM_SETINFOTIP
132  *   -- LVM_SETTILEWIDTH
133  *   -- LVM_SORTGROUPS
134  *   -- LVM_SORTITEMSEX
135  *
136  * Known differences in message stream from native control (not known if
137  * these differences cause problems):
138  *   LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
139  *   LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
140  *   WM_CREATE does not issue WM_QUERYUISTATE and associated registry
141  *     processing for "USEDOUBLECLICKTIME".
142  */
143
144 #include "config.h"
145 #include "wine/port.h"
146
147 #include <assert.h>
148 #include <ctype.h>
149 #include <string.h>
150 #include <stdlib.h>
151 #include <stdarg.h>
152 #include <stdio.h>
153
154 #include "windef.h"
155 #include "winbase.h"
156 #include "winnt.h"
157 #include "wingdi.h"
158 #include "winuser.h"
159 #include "winnls.h"
160 #include "commctrl.h"
161 #include "comctl32.h"
162
163 #include "wine/debug.h"
164 #include "wine/unicode.h"
165
166 WINE_DEFAULT_DEBUG_CHANNEL(listview);
167
168 /* make sure you set this to 0 for production use! */
169 #define DEBUG_RANGES 1
170
171 typedef struct tagCOLUMN_INFO
172 {
173   RECT rcHeader;        /* tracks the header's rectangle */
174   int fmt;              /* same as LVCOLUMN.fmt */
175 } COLUMN_INFO;
176
177 typedef struct tagITEMHDR
178 {
179   LPWSTR pszText;
180   INT iImage;
181 } ITEMHDR, *LPITEMHDR;
182
183 typedef struct tagSUBITEM_INFO
184 {
185   ITEMHDR hdr;
186   INT iSubItem;
187 } SUBITEM_INFO;
188
189 typedef struct tagITEM_INFO
190 {
191   ITEMHDR hdr;
192   UINT state;
193   LPARAM lParam;
194   INT iIndent;
195 } ITEM_INFO;
196
197 typedef struct tagRANGE
198 {
199   INT lower;
200   INT upper;
201 } RANGE;
202
203 typedef struct tagRANGES
204 {
205   HDPA hdpa;
206 } *RANGES;
207
208 typedef struct tagITERATOR
209 {
210   INT nItem;
211   INT nSpecial;
212   RANGE range;
213   RANGES ranges;
214   INT index;
215 } ITERATOR;
216
217 typedef struct tagLISTVIEW_INFO
218 {
219   HWND hwndSelf;
220   HBRUSH hBkBrush;
221   COLORREF clrBk;
222   COLORREF clrText;
223   COLORREF clrTextBk;
224   COLORREF clrTextBkDefault;
225   HIMAGELIST himlNormal;
226   HIMAGELIST himlSmall;
227   HIMAGELIST himlState;
228   BOOL bLButtonDown;
229   BOOL bRButtonDown;
230   BOOL bNoItemMetrics;          /* flags if item metrics are not yet computed */
231   INT nItemHeight;
232   INT nItemWidth;
233   RANGES selectionRanges;
234   INT nSelectionMark;
235   INT nHotItem;
236   SHORT notifyFormat;
237   HWND hwndNotify;
238   RECT rcList;                 /* This rectangle is really the window
239                                 * client rectangle possibly reduced by the 
240                                 * horizontal scroll bar and/or header - see 
241                                 * LISTVIEW_UpdateSize. This rectangle offset
242                                 * by the LISTVIEW_GetOrigin value is in
243                                 * client coordinates   */
244   SIZE iconSize;
245   SIZE iconSpacing;
246   SIZE iconStateSize;
247   UINT uCallbackMask;
248   HWND hwndHeader;
249   HCURSOR hHotCursor;
250   HFONT hDefaultFont;
251   HFONT hFont;
252   INT ntmHeight;                /* Some cached metrics of the font used */
253   INT ntmAveCharWidth;          /* by the listview to draw items */
254   BOOL bRedraw;                 /* Turns on/off repaints & invalidations */
255   BOOL bAutoarrange;            /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
256   BOOL bFocus;
257   BOOL bDoChangeNotify;                /* send change notification messages? */
258   INT nFocusedItem;
259   RECT rcFocus;
260   DWORD dwStyle;                /* the cached window GWL_STYLE */
261   DWORD dwLvExStyle;            /* extended listview style */
262   INT nItemCount;               /* the number of items in the list */
263   HDPA hdpaItems;               /* array ITEM_INFO pointers */
264   HDPA hdpaPosX;                /* maintains the (X, Y) coordinates of the */
265   HDPA hdpaPosY;                /* items in LVS_ICON, and LVS_SMALLICON modes */
266   HDPA hdpaColumns;             /* array of COLUMN_INFO pointers */
267   POINT currIconPos;            /* this is the position next icon will be placed */
268   PFNLVCOMPARE pfnCompare;
269   LPARAM lParamSort;
270   HWND hwndEdit;
271   WNDPROC EditWndProc;
272   INT nEditLabelItem;
273   DWORD dwHoverTime;
274   HWND hwndToolTip;
275
276   DWORD cditemmode;             /* Keep the custom draw flags for an item/row */
277
278   DWORD lastKeyPressTimestamp;
279   WPARAM charCode;
280   INT nSearchParamLength;
281   WCHAR szSearchParam[ MAX_PATH ];
282   BOOL bIsDrawing;
283 } LISTVIEW_INFO;
284
285 /*
286  * constants
287  */
288 /* How many we debug buffer to allocate */
289 #define DEBUG_BUFFERS 20
290 /* The size of a single debug bbuffer */
291 #define DEBUG_BUFFER_SIZE 256
292
293 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
294 #define SB_INTERNAL      -1
295
296 /* maximum size of a label */
297 #define DISP_TEXT_SIZE 512
298
299 /* padding for items in list and small icon display modes */
300 #define WIDTH_PADDING 12
301
302 /* padding for items in list, report and small icon display modes */
303 #define HEIGHT_PADDING 1
304
305 /* offset of items in report display mode */
306 #define REPORT_MARGINX 2
307
308 /* padding for icon in large icon display mode
309  *   ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
310  *                                 that HITTEST will see.
311  *   ICON_TOP_PADDING_HITABLE - spacing between above and icon.
312  *   ICON_TOP_PADDING - sum of the two above.
313  *   ICON_BOTTOM_PADDING - between bottom of icon and top of text
314  *   LABEL_HOR_PADDING - between text and sides of box
315  *   LABEL_VERT_PADDING - between bottom of text and end of box
316  *
317  *   ICON_LR_PADDING - additional width above icon size.
318  *   ICON_LR_HALF - half of the above value
319  */
320 #define ICON_TOP_PADDING_NOTHITABLE  2
321 #define ICON_TOP_PADDING_HITABLE     2
322 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
323 #define ICON_BOTTOM_PADDING          4
324 #define LABEL_HOR_PADDING            5
325 #define LABEL_VERT_PADDING           7
326 #define ICON_LR_PADDING              16
327 #define ICON_LR_HALF                 (ICON_LR_PADDING/2)
328
329 /* default label width for items in list and small icon display modes */
330 #define DEFAULT_LABEL_WIDTH 40
331
332 /* default column width for items in list display mode */
333 #define DEFAULT_COLUMN_WIDTH 128
334
335 /* Size of "line" scroll for V & H scrolls */
336 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
337
338 /* Padding betwen image and label */
339 #define IMAGE_PADDING  2
340
341 /* Padding behind the label */
342 #define TRAILING_LABEL_PADDING  12
343 #define TRAILING_HEADER_PADDING  11
344
345 /* Border for the icon caption */
346 #define CAPTION_BORDER  2
347
348 /* Standard DrawText flags */
349 #define LV_ML_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
350 #define LV_FL_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
351 #define LV_SL_DT_FLAGS  (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
352
353 /* The time in milliseconds to reset the search in the list */
354 #define KEY_DELAY       450
355
356 /* Dump the LISTVIEW_INFO structure to the debug channel */
357 #define LISTVIEW_DUMP(iP) do { \
358   TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
359         iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
360         iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
361   TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
362         iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
363         iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
364   TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
365         iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
366         iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
367   TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
368 } while(0)
369
370 /*
371  * forward declarations
372  */
373 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
374 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
375 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
376 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
377 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
378 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
379 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
380 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
381 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
382 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
383 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
384 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
385 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
386 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
387 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
388 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
389 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
390 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
391 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
392 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
393 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
394 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
395 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
396 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
397 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
398 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
399
400 /******** Text handling functions *************************************/
401
402 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
403  * text string. The string may be ANSI or Unicode, in which case
404  * the boolean isW tells us the type of the string.
405  *
406  * The name of the function tell what type of strings it expects:
407  *   W: Unicode, T: ANSI/Unicode - function of isW
408  */
409
410 static inline BOOL is_textW(LPCWSTR text)
411 {
412     return text != NULL && text != LPSTR_TEXTCALLBACKW;
413 }
414
415 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
416 {
417     /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
418     return is_textW(text);
419 }
420
421 static inline int textlenT(LPCWSTR text, BOOL isW)
422 {
423     return !is_textT(text, isW) ? 0 :
424            isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
425 }
426
427 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
428 {
429     if (isDestW)
430         if (isSrcW) lstrcpynW(dest, src, max);
431         else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
432     else
433         if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
434         else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
435 }
436
437 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
438 {
439     LPWSTR wstr = (LPWSTR)text;
440
441     if (!isW && is_textT(text, isW))
442     {
443         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
444         wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
445         if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
446     }
447     TRACE("   wstr=%s\n", text == LPSTR_TEXTCALLBACKW ?  "(callback)" : debugstr_w(wstr));
448     return wstr;
449 }
450
451 static inline void textfreeT(LPWSTR wstr, BOOL isW)
452 {
453     if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
454 }
455
456 /*
457  * dest is a pointer to a Unicode string
458  * src is a pointer to a string (Unicode if isW, ANSI if !isW)
459  */
460 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
461 {
462     BOOL bResult = TRUE;
463     
464     if (src == LPSTR_TEXTCALLBACKW)
465     {
466         if (is_textW(*dest)) Free(*dest);
467         *dest = LPSTR_TEXTCALLBACKW;
468     }
469     else
470     {
471         LPWSTR pszText = textdupTtoW(src, isW);
472         if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
473         bResult = Str_SetPtrW(dest, pszText);
474         textfreeT(pszText, isW);
475     }
476     return bResult;
477 }
478
479 /*
480  * compares a Unicode to a Unicode/ANSI text string
481  */
482 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
483 {
484     if (!aw) return bt ? -1 : 0;
485     if (!bt) return aw ? 1 : 0;
486     if (aw == LPSTR_TEXTCALLBACKW)
487         return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
488     if (bt != LPSTR_TEXTCALLBACKW)
489     {
490         LPWSTR bw = textdupTtoW(bt, isW);
491         int r = bw ? lstrcmpW(aw, bw) : 1;
492         textfreeT(bw, isW);
493         return r;
494     }       
495             
496     return 1;
497 }
498     
499 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
500 {
501     int res;
502
503     n = min(min(n, strlenW(s1)), strlenW(s2));
504     res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
505     return res ? res - sizeof(WCHAR) : res;
506 }
507
508 /******** Debugging functions *****************************************/
509
510 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
511 {
512     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
513     return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
514 }
515
516 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
517 {
518     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
519     n = min(textlenT(text, isW), n);
520     return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
521 }
522
523 static char* debug_getbuf()
524 {
525     static int index = 0;
526     static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
527     return buffers[index++ % DEBUG_BUFFERS];
528 }
529
530 static inline const char* debugrange(const RANGE *lprng)
531 {
532     if (lprng) 
533     {
534         char* buf = debug_getbuf();
535         snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
536         return buf;
537     } else return "(null)";
538 }
539
540 static inline const char* debugpoint(const POINT *lppt)
541 {
542     if (lppt) 
543     {
544         char* buf = debug_getbuf();
545         snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
546         return buf;
547     } else return "(null)";
548 }
549
550 static inline const char* debugrect(const RECT *rect)
551 {
552     if (rect) 
553     {
554         char* buf = debug_getbuf();
555         snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
556                  rect->left, rect->top, rect->right, rect->bottom);
557         return buf;
558     } else return "(null)";
559 }
560
561 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
562 {
563     char* buf = debug_getbuf(), *text = buf;
564     int len, size = DEBUG_BUFFER_SIZE;
565     
566     if (pScrollInfo == NULL) return "(null)";
567     len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
568     if (len == -1) goto end; buf += len; size -= len;
569     if (pScrollInfo->fMask & SIF_RANGE)
570         len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
571     else len = 0;
572     if (len == -1) goto end; buf += len; size -= len;
573     if (pScrollInfo->fMask & SIF_PAGE)
574         len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
575     else len = 0;
576     if (len == -1) goto end; buf += len; size -= len;
577     if (pScrollInfo->fMask & SIF_POS)
578         len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
579     else len = 0;
580     if (len == -1) goto end; buf += len; size -= len;
581     if (pScrollInfo->fMask & SIF_TRACKPOS)
582         len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
583     else len = 0;
584     if (len == -1) goto end; buf += len; size -= len;
585     goto undo;
586 end:
587     buf = text + strlen(text);
588 undo:
589     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
590     return text;
591
592
593 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
594 {
595     if (plvnm)
596     {
597         char* buf = debug_getbuf();
598         snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
599                  " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
600                  plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
601                  plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
602         return buf;
603     } else return "(null)";
604 }
605
606 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
607 {
608     char* buf = debug_getbuf(), *text = buf;
609     int len, size = DEBUG_BUFFER_SIZE;
610     
611     if (lpLVItem == NULL) return "(null)";
612     len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
613     if (len == -1) goto end; buf += len; size -= len;
614     if (lpLVItem->mask & LVIF_STATE)
615         len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
616     else len = 0;
617     if (len == -1) goto end; buf += len; size -= len;
618     if (lpLVItem->mask & LVIF_TEXT)
619         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
620     else len = 0;
621     if (len == -1) goto end; buf += len; size -= len;
622     if (lpLVItem->mask & LVIF_IMAGE)
623         len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
624     else len = 0;
625     if (len == -1) goto end; buf += len; size -= len;
626     if (lpLVItem->mask & LVIF_PARAM)
627         len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
628     else len = 0;
629     if (len == -1) goto end; buf += len; size -= len;
630     if (lpLVItem->mask & LVIF_INDENT)
631         len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
632     else len = 0;
633     if (len == -1) goto end; buf += len; size -= len;
634     goto undo;
635 end:
636     buf = text + strlen(text);
637 undo:
638     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
639     return text;
640 }
641
642 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
643 {
644     char* buf = debug_getbuf(), *text = buf;
645     int len, size = DEBUG_BUFFER_SIZE;
646     
647     if (lpColumn == NULL) return "(null)";
648     len = snprintf(buf, size, "{");
649     if (len == -1) goto end; buf += len; size -= len;
650     if (lpColumn->mask & LVCF_SUBITEM)
651         len = snprintf(buf, size, "iSubItem=%d, ",  lpColumn->iSubItem);
652     else len = 0;
653     if (len == -1) goto end; buf += len; size -= len;
654     if (lpColumn->mask & LVCF_FMT)
655         len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
656     else len = 0;
657     if (len == -1) goto end; buf += len; size -= len;
658     if (lpColumn->mask & LVCF_WIDTH)
659         len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
660     else len = 0;
661     if (len == -1) goto end; buf += len; size -= len;
662     if (lpColumn->mask & LVCF_TEXT)
663         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
664     else len = 0;
665     if (len == -1) goto end; buf += len; size -= len;
666     if (lpColumn->mask & LVCF_IMAGE)
667         len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
668     else len = 0;
669     if (len == -1) goto end; buf += len; size -= len;
670     if (lpColumn->mask & LVCF_ORDER)
671         len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
672     else len = 0;
673     if (len == -1) goto end; buf += len; size -= len;
674     goto undo;
675 end:
676     buf = text + strlen(text);
677 undo:
678     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
679     return text;
680 }
681
682 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
683 {
684     if (lpht) 
685     {
686         char* buf = debug_getbuf();
687         snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
688                  debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
689         return buf;
690     } else return "(null)";
691 }
692
693 /* Return the corresponding text for a given scroll value */
694 static inline LPCSTR debugscrollcode(int nScrollCode)
695 {
696   switch(nScrollCode)
697   {
698   case SB_LINELEFT: return "SB_LINELEFT";
699   case SB_LINERIGHT: return "SB_LINERIGHT";
700   case SB_PAGELEFT: return "SB_PAGELEFT";
701   case SB_PAGERIGHT: return "SB_PAGERIGHT";
702   case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
703   case SB_THUMBTRACK: return "SB_THUMBTRACK";
704   case SB_ENDSCROLL: return "SB_ENDSCROLL";
705   case SB_INTERNAL: return "SB_INTERNAL";
706   default: return "unknown";
707   }
708 }
709
710
711 /******** Notification functions i************************************/
712
713 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
714 {
715     LRESULT result;
716     
717     TRACE("(code=%d)\n", code);
718
719     pnmh->hwndFrom = infoPtr->hwndSelf;
720     pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
721     pnmh->code = code;
722     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
723                           (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
724
725     TRACE("  <= %ld\n", result);
726
727     return result;
728 }
729
730 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
731 {
732     NMHDR nmh;
733     return notify_hdr(infoPtr, code, &nmh);
734 }
735
736 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
737 {
738     NMITEMACTIVATE nmia;
739     LVITEMW item;
740
741     if (htInfo) {
742       nmia.uNewState = 0;
743       nmia.uOldState = 0;
744       nmia.uChanged  = 0;
745       nmia.uKeyFlags = 0;
746       
747       item.mask = LVIF_PARAM|LVIF_STATE;
748       item.iItem = htInfo->iItem;
749       item.iSubItem = 0;
750       if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
751           nmia.lParam = item.lParam;
752           nmia.uOldState = item.state;
753           nmia.uNewState = item.state | LVIS_ACTIVATING;
754           nmia.uChanged  = LVIF_STATE;
755       }
756       
757       nmia.iItem = htInfo->iItem;
758       nmia.iSubItem = htInfo->iSubItem;
759       nmia.ptAction = htInfo->pt;     
760       
761       if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
762       if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
763       if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
764     }
765     notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
766 }
767
768 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
769 {
770     TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
771     return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
772 }
773
774 static LRESULT notify_click(LISTVIEW_INFO *infoPtr,  INT code, LVHITTESTINFO *lvht)
775 {
776     NMLISTVIEW nmlv;
777     LVITEMW item;
778    
779     TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht)); 
780     ZeroMemory(&nmlv, sizeof(nmlv));
781     nmlv.iItem = lvht->iItem;
782     nmlv.iSubItem = lvht->iSubItem;
783     nmlv.ptAction = lvht->pt;
784     item.mask = LVIF_PARAM;
785     item.iItem = lvht->iItem;
786     item.iSubItem = 0;
787     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
788     return notify_listview(infoPtr, code, &nmlv);
789 }
790
791 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
792 {
793     NMLISTVIEW nmlv;
794     LVITEMW item;
795    
796     ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
797     nmlv.iItem = nItem;
798     item.mask = LVIF_PARAM;
799     item.iItem = nItem;
800     item.iSubItem = 0;
801     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
802     notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
803 }
804
805 static int get_ansi_notification(INT unicodeNotificationCode)
806 {
807     switch (unicodeNotificationCode)
808     {
809     case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
810     case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
811     case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
812     case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
813     case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
814     case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
815     }
816     ERR("unknown notification %x\n", unicodeNotificationCode);
817     assert(FALSE);
818     return 0;
819 }
820
821 /*
822   With testing on Windows 2000 it looks like the notify format
823   has nothing to do with this message. It ALWAYS seems to be
824   in ansi format.
825
826   infoPtr : listview struct
827   notificationCode : *Unicode* notification code
828   pdi : dispinfo structure (can be unicode or ansi)
829   isW : TRUE if dispinfo is Unicode
830 */
831 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
832 {
833     BOOL bResult = FALSE;
834     BOOL convertToAnsi = FALSE;
835     INT cchTempBufMax = 0, savCchTextMax = 0;
836     LPWSTR pszTempBuf = NULL, savPszText = NULL;
837
838     if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
839             convertToAnsi = isW;
840
841     if (convertToAnsi)
842     {
843         if (notificationCode != LVN_GETDISPINFOW)
844         {
845             cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
846                                                 -1, NULL, 0, NULL, NULL);
847         }
848         else
849         {
850             cchTempBufMax = pdi->item.cchTextMax;
851             *pdi->item.pszText = 0; /* make sure we don't process garbage */
852         }
853
854         pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
855                                cchTempBufMax);
856         if (!pszTempBuf) return FALSE;
857
858         WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
859                              pszTempBuf, cchTempBufMax, NULL, NULL);
860
861         savCchTextMax = pdi->item.cchTextMax;
862         savPszText = pdi->item.pszText;
863         pdi->item.pszText = pszTempBuf;
864         pdi->item.cchTextMax = cchTempBufMax;
865     }
866
867     TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
868            NFR_ANSI));
869
870     bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
871                         (LPNMHDR)pdi);
872
873     if (convertToAnsi)
874     {
875         MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
876                             savPszText, savCchTextMax);
877         pdi->item.pszText = savPszText; /* restores our buffer */
878         pdi->item.cchTextMax = savCchTextMax;
879         HeapFree(GetProcessHeap(), 0, pszTempBuf);
880     }
881     return bResult;
882 }
883
884 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, 
885                             const RECT *rcBounds, const LVITEMW *lplvItem)
886 {
887     ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
888     lpnmlvcd->nmcd.hdc = hdc;
889     lpnmlvcd->nmcd.rc = *rcBounds;
890     lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
891     lpnmlvcd->clrText   = infoPtr->clrText;
892     if (!lplvItem) return;
893     lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
894     lpnmlvcd->iSubItem = lplvItem->iSubItem;
895     if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
896     if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
897     if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
898     lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
899 }
900
901 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
902 {
903     BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
904     DWORD result;
905
906     lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
907     if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM; 
908     if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
909     if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
910     result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
911     if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
912     return result;
913 }
914
915 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
916 {
917     /* apprently, for selected items, we have to override the returned values */
918     if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
919     {
920         if (infoPtr->bFocus)
921         {
922             lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
923             lpnmlvcd->clrText   = comctl32_color.clrHighlightText;
924         }
925         else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
926         {
927             lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
928             lpnmlvcd->clrText   = comctl32_color.clrBtnText;
929         }
930     }
931
932     /* Set the text attributes */
933     if (lpnmlvcd->clrTextBk != CLR_NONE)
934     {
935         SetBkMode(hdc, OPAQUE);
936         if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
937             SetBkColor(hdc, infoPtr->clrTextBkDefault);
938         else
939             SetBkColor(hdc,lpnmlvcd->clrTextBk);
940     }
941     else
942         SetBkMode(hdc, TRANSPARENT);
943     SetTextColor(hdc, lpnmlvcd->clrText);
944 }
945
946 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
947 {
948     return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
949 }
950
951 /******** Item iterator functions **********************************/
952
953 static RANGES ranges_create(int count);
954 static void ranges_destroy(RANGES ranges);
955 static BOOL ranges_add(RANGES ranges, RANGE range);
956 static BOOL ranges_del(RANGES ranges, RANGE range);
957 static void ranges_dump(RANGES ranges);
958
959 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
960 {
961     RANGE range = { nItem, nItem + 1 };
962
963     return ranges_add(ranges, range);
964 }
965
966 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
967 {
968     RANGE range = { nItem, nItem + 1 };
969
970     return ranges_del(ranges, range);
971 }
972
973 /***
974  * ITERATOR DOCUMENTATION
975  *
976  * The iterator functions allow for easy, and convenient iteration
977  * over items of iterest in the list. Typically, you create a
978  * iterator, use it, and destroy it, as such:
979  *   ITERATOR i;
980  *
981  *   iterator_xxxitems(&i, ...);
982  *   while (iterator_{prev,next}(&i)
983  *   {
984  *       //code which uses i.nItem
985  *   }
986  *   iterator_destroy(&i);
987  *
988  *   where xxx is either: framed, or visible.
989  * Note that it is important that the code destroys the iterator
990  * after it's done with it, as the creation of the iterator may
991  * allocate memory, which thus needs to be freed.
992  * 
993  * You can iterate both forwards, and backwards through the list,
994  * by using iterator_next or iterator_prev respectively.
995  * 
996  * Lower numbered items are draw on top of higher number items in
997  * LVS_ICON, and LVS_SMALLICON (which are the only modes where
998  * items may overlap). So, to test items, you should use
999  *    iterator_next
1000  * which lists the items top to bottom (in Z-order).
1001  * For drawing items, you should use
1002  *    iterator_prev
1003  * which lists the items bottom to top (in Z-order).
1004  * If you keep iterating over the items after the end-of-items
1005  * marker (-1) is returned, the iterator will start from the
1006  * beginning. Typically, you don't need to test for -1,
1007  * because iterator_{next,prev} will return TRUE if more items
1008  * are to be iterated over, or FALSE otherwise.
1009  *
1010  * Note: the iterator is defined to be bidirectional. That is,
1011  *       any number of prev followed by any number of next, or
1012  *       five versa, should leave the iterator at the same item:
1013  *           prev * n, next * n = next * n, prev * n
1014  *
1015  * The iterator has a notion of a out-of-order, special item,
1016  * which sits at the start of the list. This is used in
1017  * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1018  * which needs to be first, as it may overlap other items.
1019  *           
1020  * The code is a bit messy because we have:
1021  *   - a special item to deal with
1022  *   - simple range, or composite range
1023  *   - empty range.
1024  * If you find bugs, or want to add features, please make sure you
1025  * always check/modify *both* iterator_prev, and iterator_next.
1026  */
1027
1028 /****
1029  * This function iterates through the items in increasing order,
1030  * but prefixed by the special item, then -1. That is:
1031  *    special, 1, 2, 3, ..., n, -1.
1032  * Each item is listed only once.
1033  */
1034 static inline BOOL iterator_next(ITERATOR* i)
1035 {
1036     if (i->nItem == -1)
1037     {
1038         i->nItem = i->nSpecial;
1039         if (i->nItem != -1) return TRUE;
1040     }
1041     if (i->nItem == i->nSpecial)
1042     {
1043         if (i->ranges) i->index = 0;
1044         goto pickarange;
1045     }
1046
1047     i->nItem++;
1048 testitem:
1049     if (i->nItem == i->nSpecial) i->nItem++;
1050     if (i->nItem < i->range.upper) return TRUE;
1051
1052 pickarange:
1053     if (i->ranges)
1054     {
1055         if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1056             i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1057         else goto end;
1058     }
1059     else if (i->nItem >= i->range.upper) goto end;
1060
1061     i->nItem = i->range.lower;
1062     if (i->nItem >= 0) goto testitem;
1063 end:
1064     i->nItem = -1;
1065     return FALSE;
1066 }
1067
1068 /****
1069  * This function iterates through the items in decreasing order,
1070  * followed by the special item, then -1. That is:
1071  *    n, n-1, ..., 3, 2, 1, special, -1.
1072  * Each item is listed only once.
1073  */
1074 static inline BOOL iterator_prev(ITERATOR* i)
1075 {
1076     BOOL start = FALSE;
1077
1078     if (i->nItem == -1)
1079     {
1080         start = TRUE;
1081         if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1082         goto pickarange;
1083     }
1084     if (i->nItem == i->nSpecial)
1085     {
1086         i->nItem = -1;
1087         return FALSE;
1088     }
1089
1090 testitem:
1091     i->nItem--;
1092     if (i->nItem == i->nSpecial) i->nItem--;
1093     if (i->nItem >= i->range.lower) return TRUE;
1094
1095 pickarange:
1096     if (i->ranges)
1097     {
1098         if (i->index > 0)
1099             i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1100         else goto end;
1101     }
1102     else if (!start && i->nItem < i->range.lower) goto end;
1103
1104     i->nItem = i->range.upper;
1105     if (i->nItem > 0) goto testitem;
1106 end:
1107     return (i->nItem = i->nSpecial) != -1;
1108 }
1109
1110 static RANGE iterator_range(ITERATOR* i)
1111 {
1112     RANGE range;
1113
1114     if (!i->ranges) return i->range;
1115
1116     if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1117     {
1118         range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1119         range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1120     }
1121     else range.lower = range.upper = 0;
1122
1123     return range;
1124 }
1125
1126 /***
1127  * Releases resources associated with this ierator.
1128  */
1129 static inline void iterator_destroy(ITERATOR* i)
1130 {
1131     ranges_destroy(i->ranges);
1132 }
1133
1134 /***
1135  * Create an empty iterator.
1136  */
1137 static inline BOOL iterator_empty(ITERATOR* i)
1138 {
1139     ZeroMemory(i, sizeof(*i));
1140     i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1141     return TRUE;
1142 }
1143
1144 /***
1145  * Create an iterator over a range.
1146  */
1147 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1148 {
1149     iterator_empty(i);
1150     i->range = range;
1151     return TRUE;
1152 }
1153
1154 /***
1155  * Create an iterator over a bunch of ranges.
1156  * Please note that the iterator will take ownership of the ranges,
1157  * and will free them upon destruction.
1158  */
1159 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1160 {
1161     iterator_empty(i);
1162     i->ranges = ranges;
1163     return TRUE;
1164 }
1165
1166 /***
1167  * Creates an iterator over the items which intersect lprc.
1168  */
1169 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1170 {
1171     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1172     RECT frame = *lprc, rcItem, rcTemp;
1173     POINT Origin;
1174     
1175     /* in case we fail, we want to return an empty iterator */
1176     if (!iterator_empty(i)) return FALSE;
1177
1178     LISTVIEW_GetOrigin(infoPtr, &Origin);
1179
1180     TRACE("(lprc=%s)\n", debugrect(lprc));
1181     OffsetRect(&frame, -Origin.x, -Origin.y);
1182
1183     if (uView == LVS_ICON || uView == LVS_SMALLICON)
1184     {
1185         INT nItem;
1186         
1187         if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1188         {
1189             LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1190             if (IntersectRect(&rcTemp, &rcItem, lprc))
1191                 i->nSpecial = infoPtr->nFocusedItem;
1192         }
1193         if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1194         /* to do better here, we need to have PosX, and PosY sorted */
1195         TRACE("building icon ranges:\n");
1196         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1197         {
1198             rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1199             rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1200             rcItem.right = rcItem.left + infoPtr->nItemWidth;
1201             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1202             if (IntersectRect(&rcTemp, &rcItem, &frame))
1203                 ranges_additem(i->ranges, nItem);
1204         }
1205         return TRUE;
1206     }
1207     else if (uView == LVS_REPORT)
1208     {
1209         RANGE range;
1210         
1211         if (frame.left >= infoPtr->nItemWidth) return TRUE;
1212         if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1213         
1214         range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1215         range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1216         if (range.upper <= range.lower) return TRUE;
1217         if (!iterator_rangeitems(i, range)) return FALSE;
1218         TRACE("    report=%s\n", debugrange(&i->range));
1219     }
1220     else
1221     {
1222         INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1223         INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1224         INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1225         INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1226         INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1227         INT lower = nFirstCol * nPerCol + nFirstRow;
1228         RANGE item_range;
1229         INT nCol;
1230
1231         TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1232               nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1233         
1234         if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1235
1236         if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1237         TRACE("building list ranges:\n");
1238         for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1239         {
1240             item_range.lower = nCol * nPerCol + nFirstRow;
1241             if(item_range.lower >= infoPtr->nItemCount) break;
1242             item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1243             TRACE("   list=%s\n", debugrange(&item_range));
1244             ranges_add(i->ranges, item_range);
1245         }
1246     }
1247
1248     return TRUE;
1249 }
1250
1251 /***
1252  * Creates an iterator over the items which intersect the visible region of hdc.
1253  */
1254 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC  hdc)
1255 {
1256     POINT Origin, Position;
1257     RECT rcItem, rcClip;
1258     INT rgntype;
1259     
1260     rgntype = GetClipBox(hdc, &rcClip);
1261     if (rgntype == NULLREGION) return iterator_empty(i);
1262     if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1263     if (rgntype == SIMPLEREGION) return TRUE;
1264
1265     /* first deal with the special item */
1266     if (i->nSpecial != -1)
1267     {
1268         LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1269         if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1270     }
1271     
1272     /* if we can't deal with the region, we'll just go with the simple range */
1273     LISTVIEW_GetOrigin(infoPtr, &Origin);
1274     TRACE("building visible range:\n");
1275     if (!i->ranges && i->range.lower < i->range.upper)
1276     {
1277         if (!(i->ranges = ranges_create(50))) return TRUE;
1278         if (!ranges_add(i->ranges, i->range))
1279         {
1280             ranges_destroy(i->ranges);
1281             i->ranges = 0;
1282             return TRUE;
1283         }
1284     }
1285
1286     /* now delete the invisible items from the list */
1287     while(iterator_next(i))
1288     {
1289         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1290         rcItem.left = Position.x + Origin.x;
1291         rcItem.top = Position.y + Origin.y;
1292         rcItem.right = rcItem.left + infoPtr->nItemWidth;
1293         rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1294         if (!RectVisible(hdc, &rcItem))
1295             ranges_delitem(i->ranges, i->nItem);
1296     }
1297     /* the iterator should restart on the next iterator_next */
1298     TRACE("done\n");
1299     
1300     return TRUE;
1301 }
1302
1303 /******** Misc helper functions ************************************/
1304
1305 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1306                                       WPARAM wParam, LPARAM lParam, BOOL isW)
1307 {
1308     if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1309     else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1310 }
1311
1312 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1313 {
1314     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1315     
1316     return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1317            (uView == LVS_ICON || uView == LVS_SMALLICON);
1318 }
1319
1320 /******** Internal API functions ************************************/
1321
1322 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1323 {
1324     static COLUMN_INFO mainItem;
1325
1326     if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1327     assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1328     return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1329 }
1330         
1331 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1332 {
1333     *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1334 }
1335         
1336 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1337 {
1338     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1339 }
1340
1341 /* Listview invalidation functions: use _only_ these functions to invalidate */
1342
1343 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1344 {
1345     return infoPtr->bRedraw;
1346 }
1347
1348 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1349 {
1350     if(!is_redrawing(infoPtr)) return; 
1351     TRACE(" invalidating rect=%s\n", debugrect(rect));
1352     InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1353 }
1354
1355 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1356 {
1357     RECT rcBox;
1358
1359     if(!is_redrawing(infoPtr)) return; 
1360     LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1361     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1362 }
1363
1364 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1365 {
1366     POINT Origin, Position;
1367     RECT rcBox;
1368     
1369     if(!is_redrawing(infoPtr)) return; 
1370     assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1371     LISTVIEW_GetOrigin(infoPtr, &Origin);
1372     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1373     LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1374     rcBox.top = 0;
1375     rcBox.bottom = infoPtr->nItemHeight;
1376     OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1377     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1378 }
1379
1380 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1381 {
1382     LISTVIEW_InvalidateRect(infoPtr, NULL);
1383 }
1384
1385 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1386 {
1387     RECT rcCol;
1388     
1389     if(!is_redrawing(infoPtr)) return; 
1390     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1391     rcCol.top = infoPtr->rcList.top;
1392     rcCol.bottom = infoPtr->rcList.bottom;
1393     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1394 }
1395
1396 /***
1397  * DESCRIPTION:
1398  * Retrieves the number of items that can fit vertically in the client area.
1399  *
1400  * PARAMETER(S):
1401  * [I] infoPtr : valid pointer to the listview structure
1402  *
1403  * RETURN:
1404  * Number of items per row.
1405  */
1406 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1407 {
1408     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1409
1410     return max(nListWidth/infoPtr->nItemWidth, 1);
1411 }
1412
1413 /***
1414  * DESCRIPTION:
1415  * Retrieves the number of items that can fit horizontally in the client
1416  * area.
1417  *
1418  * PARAMETER(S):
1419  * [I] infoPtr : valid pointer to the listview structure
1420  *
1421  * RETURN:
1422  * Number of items per column.
1423  */
1424 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1425 {
1426     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1427
1428     return max(nListHeight / infoPtr->nItemHeight, 1);
1429 }
1430
1431
1432 /*************************************************************************
1433  *              LISTVIEW_ProcessLetterKeys
1434  *
1435  *  Processes keyboard messages generated by pressing the letter keys
1436  *  on the keyboard.
1437  *  What this does is perform a case insensitive search from the
1438  *  current position with the following quirks:
1439  *  - If two chars or more are pressed in quick succession we search
1440  *    for the corresponding string (e.g. 'abc').
1441  *  - If there is a delay we wipe away the current search string and
1442  *    restart with just that char.
1443  *  - If the user keeps pressing the same character, whether slowly or
1444  *    fast, so that the search string is entirely composed of this
1445  *    character ('aaaaa' for instance), then we search for first item
1446  *    that starting with that character.
1447  *  - If the user types the above character in quick succession, then
1448  *    we must also search for the corresponding string ('aaaaa'), and
1449  *    go to that string if there is a match.
1450  *
1451  * PARAMETERS
1452  *   [I] hwnd : handle to the window
1453  *   [I] charCode : the character code, the actual character
1454  *   [I] keyData : key data
1455  *
1456  * RETURNS
1457  *
1458  *  Zero.
1459  *
1460  * BUGS
1461  *
1462  *  - The current implementation has a list of characters it will
1463  *    accept and it ignores averything else. In particular it will
1464  *    ignore accentuated characters which seems to match what
1465  *    Windows does. But I'm not sure it makes sense to follow
1466  *    Windows there.
1467  *  - We don't sound a beep when the search fails.
1468  *
1469  * SEE ALSO
1470  *
1471  *  TREEVIEW_ProcessLetterKeys
1472  */
1473 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1474 {
1475     INT nItem;
1476     INT endidx,idx;
1477     LVITEMW item;
1478     WCHAR buffer[MAX_PATH];
1479     DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1480
1481     /* simple parameter checking */
1482     if (!charCode || !keyData) return 0;
1483
1484     /* only allow the valid WM_CHARs through */
1485     if (!isalnum(charCode) &&
1486         charCode != '.' && charCode != '`' && charCode != '!' &&
1487         charCode != '@' && charCode != '#' && charCode != '$' &&
1488         charCode != '%' && charCode != '^' && charCode != '&' &&
1489         charCode != '*' && charCode != '(' && charCode != ')' &&
1490         charCode != '-' && charCode != '_' && charCode != '+' &&
1491         charCode != '=' && charCode != '\\'&& charCode != ']' &&
1492         charCode != '}' && charCode != '[' && charCode != '{' &&
1493         charCode != '/' && charCode != '?' && charCode != '>' &&
1494         charCode != '<' && charCode != ',' && charCode != '~')
1495         return 0;
1496
1497     /* if there's one item or less, there is no where to go */
1498     if (infoPtr->nItemCount <= 1) return 0;
1499
1500     /* update the search parameters */
1501     infoPtr->lastKeyPressTimestamp = GetTickCount();
1502     if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1503         if (infoPtr->nSearchParamLength < MAX_PATH)
1504             infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1505         if (infoPtr->charCode != charCode)
1506             infoPtr->charCode = charCode = 0;
1507     } else {
1508         infoPtr->charCode=charCode;
1509         infoPtr->szSearchParam[0]=charCode;
1510         infoPtr->nSearchParamLength=1;
1511         /* Redundant with the 1 char string */
1512         charCode=0;
1513     }
1514
1515     /* and search from the current position */
1516     nItem=-1;
1517     if (infoPtr->nFocusedItem >= 0) {
1518         endidx=infoPtr->nFocusedItem;
1519         idx=endidx;
1520         /* if looking for single character match,
1521          * then we must always move forward
1522          */
1523         if (infoPtr->nSearchParamLength == 1)
1524             idx++;
1525     } else {
1526         endidx=infoPtr->nItemCount;
1527         idx=0;
1528     }
1529     do {
1530         if (idx == infoPtr->nItemCount) {
1531             if (endidx == infoPtr->nItemCount || endidx == 0)
1532                 break;
1533             idx=0;
1534         }
1535
1536         /* get item */
1537         item.mask = LVIF_TEXT;
1538         item.iItem = idx;
1539         item.iSubItem = 0;
1540         item.pszText = buffer;
1541         item.cchTextMax = MAX_PATH;
1542         if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1543
1544         /* check for a match */
1545         if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1546             nItem=idx;
1547             break;
1548         } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1549                     (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1550             /* This would work but we must keep looking for a longer match */
1551             nItem=idx;
1552         }
1553         idx++;
1554     } while (idx != endidx);
1555
1556     if (nItem != -1)
1557         LISTVIEW_KeySelection(infoPtr, nItem);
1558
1559     return 0;
1560 }
1561
1562 /*************************************************************************
1563  * LISTVIEW_UpdateHeaderSize [Internal]
1564  *
1565  * Function to resize the header control
1566  *
1567  * PARAMS
1568  * [I]  hwnd : handle to a window
1569  * [I]  nNewScrollPos : scroll pos to set
1570  *
1571  * RETURNS
1572  * None.
1573  */
1574 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1575 {
1576     RECT winRect;
1577     POINT point[2];
1578
1579     TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1580
1581     GetWindowRect(infoPtr->hwndHeader, &winRect);
1582     point[0].x = winRect.left;
1583     point[0].y = winRect.top;
1584     point[1].x = winRect.right;
1585     point[1].y = winRect.bottom;
1586
1587     MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1588     point[0].x = -nNewScrollPos;
1589     point[1].x += nNewScrollPos;
1590
1591     SetWindowPos(infoPtr->hwndHeader,0,
1592         point[0].x,point[0].y,point[1].x,point[1].y,
1593         SWP_NOZORDER | SWP_NOACTIVATE);
1594 }
1595
1596 /***
1597  * DESCRIPTION:
1598  * Update the scrollbars. This functions should be called whenever
1599  * the content, size or view changes.
1600  *
1601  * PARAMETER(S):
1602  * [I] infoPtr : valid pointer to the listview structure
1603  *
1604  * RETURN:
1605  * None
1606  */
1607 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1608 {
1609     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1610     SCROLLINFO horzInfo, vertInfo;
1611
1612     if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1613
1614     ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1615     horzInfo.cbSize = sizeof(SCROLLINFO);
1616     horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1617
1618     /* for now, we'll set info.nMax to the _count_, and adjust it later */
1619     if (uView == LVS_LIST)
1620     {
1621         INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1622         horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1623
1624         /* scroll by at least one column per page */
1625         if(horzInfo.nPage < infoPtr->nItemWidth)
1626                 horzInfo.nPage = infoPtr->nItemWidth;
1627
1628         horzInfo.nPage /= infoPtr->nItemWidth;
1629     }
1630     else if (uView == LVS_REPORT)
1631     {
1632         horzInfo.nMax = infoPtr->nItemWidth;
1633     }
1634     else /* LVS_ICON, or LVS_SMALLICON */
1635     {
1636         RECT rcView;
1637
1638         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1639     }
1640   
1641     horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1642     horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1643     SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1644     TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1645
1646     /* Setting the horizontal scroll can change the listview size
1647      * (and potentially everything else) so we need to recompute
1648      * everything again for the vertical scroll
1649      */
1650
1651     ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1652     vertInfo.cbSize = sizeof(SCROLLINFO);
1653     vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1654
1655     if (uView == LVS_REPORT)
1656     {
1657         vertInfo.nMax = infoPtr->nItemCount;
1658         
1659         /* scroll by at least one page */
1660         if(vertInfo.nPage < infoPtr->nItemHeight)
1661           vertInfo.nPage = infoPtr->nItemHeight;
1662
1663         vertInfo.nPage /= infoPtr->nItemHeight;
1664     }
1665     else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1666     {
1667         RECT rcView;
1668
1669         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1670     }
1671
1672     vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1673     vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1674     SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1675     TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1676
1677     /* Update the Header Control */
1678     if (uView == LVS_REPORT)
1679     {
1680         horzInfo.fMask = SIF_POS;
1681         GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1682         LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1683     }
1684 }
1685
1686
1687 /***
1688  * DESCRIPTION:
1689  * Shows/hides the focus rectangle. 
1690  *
1691  * PARAMETER(S):
1692  * [I] infoPtr : valid pointer to the listview structure
1693  * [I] fShow : TRUE to show the focus, FALSE to hide it.
1694  *
1695  * RETURN:
1696  * None
1697  */
1698 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1699 {
1700     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1701     HDC hdc;
1702
1703     TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1704
1705     if (infoPtr->nFocusedItem < 0) return;
1706
1707     /* we need some gymnastics in ICON mode to handle large items */
1708     if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1709     {
1710         RECT rcBox;
1711
1712         LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox); 
1713         if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1714         {
1715             LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1716             return;
1717         }
1718     }
1719
1720     if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1721
1722     /* for some reason, owner draw should work only in report mode */
1723     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1724     {
1725         DRAWITEMSTRUCT dis;
1726         LVITEMW item;
1727
1728         item.iItem = infoPtr->nFocusedItem;
1729         item.iSubItem = 0;
1730         item.mask = LVIF_PARAM;
1731         if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1732            
1733         ZeroMemory(&dis, sizeof(dis)); 
1734         dis.CtlType = ODT_LISTVIEW;
1735         dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1736         dis.itemID = item.iItem;
1737         dis.itemAction = ODA_FOCUS;
1738         if (fShow) dis.itemState |= ODS_FOCUS;
1739         dis.hwndItem = infoPtr->hwndSelf;
1740         dis.hDC = hdc;
1741         LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1742         dis.itemData = item.lParam;
1743
1744         SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1745     }
1746     else
1747     {
1748         DrawFocusRect(hdc, &infoPtr->rcFocus);
1749     }
1750 done:
1751     ReleaseDC(infoPtr->hwndSelf, hdc);
1752 }
1753
1754 /***
1755  * Invalidates all visible selected items.
1756  */
1757 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1758 {
1759     ITERATOR i; 
1760    
1761     iterator_frameditems(&i, infoPtr, &infoPtr->rcList); 
1762     while(iterator_next(&i))
1763     {
1764         if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1765             LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1766     }
1767     iterator_destroy(&i);
1768 }
1769
1770             
1771 /***
1772  * DESCRIPTION:            [INTERNAL]
1773  * Computes an item's (left,top) corner, relative to rcView.
1774  * That is, the position has NOT been made relative to the Origin.
1775  * This is deliberate, to avoid computing the Origin over, and
1776  * over again, when this function is call in a loop. Instead,
1777  * one ca factor the computation of the Origin before the loop,
1778  * and offset the value retured by this function, on every iteration.
1779  * 
1780  * PARAMETER(S):
1781  * [I] infoPtr : valid pointer to the listview structure
1782  * [I] nItem  : item number
1783  * [O] lpptOrig : item top, left corner
1784  *
1785  * RETURN:
1786  *   None.
1787  */
1788 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1789 {
1790     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1791
1792     assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1793
1794     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1795     {
1796         lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1797         lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1798     }
1799     else if (uView == LVS_LIST)
1800     {
1801         INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1802         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1803         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1804     }
1805     else /* LVS_REPORT */
1806     {
1807         lpptPosition->x = 0;
1808         lpptPosition->y = nItem * infoPtr->nItemHeight;
1809     }
1810 }
1811     
1812 /***
1813  * DESCRIPTION:            [INTERNAL]
1814  * Compute the rectangles of an item.  This is to localize all
1815  * the computations in one place. If you are not interested in some
1816  * of these values, simply pass in a NULL -- the fucntion is smart
1817  * enough to compute only what's necessary. The function computes
1818  * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1819  * one, the BOX rectangle. This rectangle is very cheap to compute,
1820  * and is guaranteed to contain all the other rectangles. Computing
1821  * the ICON rect is also cheap, but all the others are potentaily
1822  * expensive. This gives an easy and effective optimization when
1823  * searching (like point inclusion, or rectangle intersection):
1824  * first test against the BOX, and if TRUE, test agains the desired
1825  * rectangle.
1826  * If the function does not have all the necessary information
1827  * to computed the requested rectangles, will crash with a
1828  * failed assertion. This is done so we catch all programming
1829  * errors, given that the function is called only from our code.
1830  *
1831  * We have the following 'special' meanings for a few fields:
1832  *   * If LVIS_FOCUSED is set, we assume the item has the focus
1833  *     This is important in ICON mode, where it might get a larger
1834  *     then usual rectange
1835  *
1836  * Please note that subitem support works only in REPORT mode.
1837  *
1838  * PARAMETER(S):
1839  * [I] infoPtr : valid pointer to the listview structure
1840  * [I] lpLVItem : item to compute the measures for
1841  * [O] lprcBox : ptr to Box rectangle
1842  *                The internal LVIR_BOX rectangle
1843  * [0] lprcState : ptr to State icon rectangle
1844  *                The internal LVIR_STATE rectangle
1845  * [O] lprcIcon : ptr to Icon rectangle
1846  *                Same as LVM_GETITEMRECT with LVIR_ICON
1847  * [O] lprcLabel : ptr to Label rectangle
1848  *                Same as LVM_GETITEMRECT with LVIR_LABEL
1849  *
1850  * RETURN:
1851  *   None.
1852  */
1853 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1854                                     LPRECT lprcBox, LPRECT lprcState, 
1855                                     LPRECT lprcIcon, LPRECT lprcLabel)
1856 {
1857     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1858     BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1859     RECT Box, State, Icon, Label;
1860     COLUMN_INFO *lpColumnInfo = NULL;
1861
1862     TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1863         
1864     /* Be smart and try to figure out the minimum we have to do */
1865     if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1866     if (uView == LVS_ICON && (lprcBox || lprcLabel))
1867     {
1868         assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1869         if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1870     }
1871     if (lprcLabel) doLabel = TRUE;
1872     if (doLabel || lprcIcon) doIcon = TRUE;
1873     if (doIcon || lprcState) doState = TRUE;
1874     
1875     /************************************************************/
1876     /* compute the box rectangle (it should be cheap to do)     */
1877     /************************************************************/
1878     if (lpLVItem->iSubItem || uView == LVS_REPORT)
1879         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1880
1881     if (lpLVItem->iSubItem)    
1882     {
1883         Box = lpColumnInfo->rcHeader;
1884     }
1885     else
1886     {
1887         Box.left = 0;
1888         Box.right = infoPtr->nItemWidth;
1889     }
1890     Box.top = 0;
1891     Box.bottom = infoPtr->nItemHeight;
1892                 
1893     /************************************************************/
1894     /* compute STATEICON bounding box                           */
1895     /************************************************************/
1896     if (doState)
1897     {
1898         if (uView == LVS_ICON)
1899         {
1900             State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1901             if (infoPtr->himlNormal) 
1902                 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1903             State.top  = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1904         }
1905         else
1906         {
1907             /* we need the ident in report mode, if we don't have it, we fail */
1908             State.left = Box.left;
1909             if (uView == LVS_REPORT) 
1910             {
1911                 if (lpLVItem->iSubItem == 0)
1912                 {
1913                     State.left += REPORT_MARGINX;
1914                     assert(lpLVItem->mask & LVIF_INDENT);
1915                     State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1916                 }
1917             }
1918             State.top  = Box.top;
1919         }       
1920         State.right    = State.left;
1921         State.bottom   = State.top;
1922         if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1923         {
1924             State.right  += infoPtr->iconStateSize.cx;
1925             State.bottom += infoPtr->iconStateSize.cy;
1926         }
1927         if (lprcState) *lprcState = State;
1928         TRACE("    - state=%s\n", debugrect(&State));
1929     }
1930
1931     /************************************************************/
1932     /* compute ICON bounding box (ala LVM_GETITEMRECT)          */
1933     /************************************************************/
1934     if (doIcon)
1935     {
1936         if (uView == LVS_ICON)
1937         {
1938             Icon.left   = Box.left;
1939             if (infoPtr->himlNormal) 
1940                 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1941             Icon.top    = Box.top + ICON_TOP_PADDING;
1942             Icon.right  = Icon.left;
1943             Icon.bottom = Icon.top;
1944             if (infoPtr->himlNormal)
1945             {
1946                 Icon.right  += infoPtr->iconSize.cx;
1947                 Icon.bottom += infoPtr->iconSize.cy;
1948             }
1949         }
1950         else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1951         {
1952             Icon.left = State.right;
1953             Icon.top    = Box.top;
1954             Icon.right  = Icon.left;
1955             if (infoPtr->himlSmall &&
1956                 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1957                  ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1958                 Icon.right += infoPtr->iconSize.cx;
1959             Icon.bottom = Icon.top + infoPtr->nItemHeight;
1960         }
1961         if(lprcIcon) *lprcIcon = Icon;
1962         TRACE("    - icon=%s\n", debugrect(&Icon));
1963      }
1964
1965     /************************************************************/
1966     /* compute LABEL bounding box (ala LVM_GETITEMRECT)         */
1967     /************************************************************/
1968     if (doLabel)
1969     {
1970         SIZE labelSize = { 0, 0 };
1971
1972         /* calculate how far to the right can the label strech */
1973         Label.right = Box.right;
1974         if (uView == LVS_REPORT)
1975         {
1976             if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1977         }
1978
1979         if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1980         {
1981            labelSize.cx = infoPtr->nItemWidth;
1982            labelSize.cy = infoPtr->nItemHeight;
1983            goto calc_label;
1984         }
1985         
1986         /* we need the text in non owner draw mode */
1987         assert(lpLVItem->mask & LVIF_TEXT);
1988         if (is_textT(lpLVItem->pszText, TRUE))
1989         {
1990             HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1991             HDC hdc = GetDC(infoPtr->hwndSelf);
1992             HFONT hOldFont = SelectObject(hdc, hFont);
1993             UINT uFormat;
1994             RECT rcText;
1995
1996             /* compute rough rectangle where the label will go */
1997             SetRectEmpty(&rcText);
1998             rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1999             rcText.bottom = infoPtr->nItemHeight;
2000             if (uView == LVS_ICON) 
2001                 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2002
2003             /* now figure out the flags */
2004             if (uView == LVS_ICON)
2005                 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2006             else
2007                 uFormat = LV_SL_DT_FLAGS;
2008             
2009             DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2010
2011             labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2012             labelSize.cy = rcText.bottom - rcText.top;
2013
2014             SelectObject(hdc, hOldFont);
2015             ReleaseDC(infoPtr->hwndSelf, hdc);
2016         }
2017
2018 calc_label:
2019         if (uView == LVS_ICON)
2020         {
2021             Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2022             Label.top  = Box.top + ICON_TOP_PADDING_HITABLE +
2023                          infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2024             Label.right = Label.left + labelSize.cx;
2025             Label.bottom = Label.top + infoPtr->nItemHeight;
2026             if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2027             {
2028                 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2029                 labelSize.cy /= infoPtr->ntmHeight;
2030                 labelSize.cy = max(labelSize.cy, 1);
2031                 labelSize.cy *= infoPtr->ntmHeight;
2032              }
2033              Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2034         }
2035         else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2036         {
2037             Label.left = Icon.right;
2038             Label.top = Box.top;
2039             Label.right = min(Label.left + labelSize.cx, Label.right);
2040             Label.bottom = Label.top + infoPtr->nItemHeight;
2041         }
2042   
2043         if (lprcLabel) *lprcLabel = Label;
2044         TRACE("    - label=%s\n", debugrect(&Label));
2045     }
2046
2047     /* Fix the Box if necessary */
2048     if (lprcBox)
2049     {
2050         if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2051         else *lprcBox = Box;
2052     }
2053     TRACE("    - box=%s\n", debugrect(&Box));
2054 }
2055
2056 /***
2057  * DESCRIPTION:            [INTERNAL]
2058  *
2059  * PARAMETER(S):
2060  * [I] infoPtr : valid pointer to the listview structure
2061  * [I] nItem : item number
2062  * [O] lprcBox : ptr to Box rectangle
2063  *
2064  * RETURN:
2065  *   None.
2066  */
2067 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2068 {
2069     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2070     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2071     POINT Position, Origin;
2072     LVITEMW lvItem;
2073
2074     LISTVIEW_GetOrigin(infoPtr, &Origin);
2075     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2076
2077     /* Be smart and try to figure out the minimum we have to do */
2078     lvItem.mask = 0;
2079     if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2080         lvItem.mask |= LVIF_TEXT;
2081     lvItem.iItem = nItem;
2082     lvItem.iSubItem = 0;
2083     lvItem.pszText = szDispText;
2084     lvItem.cchTextMax = DISP_TEXT_SIZE;
2085     if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2086     if (uView == LVS_ICON)
2087     {
2088         lvItem.mask |= LVIF_STATE;
2089         lvItem.stateMask = LVIS_FOCUSED;
2090         lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2091     }
2092     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2093
2094     OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2095 }
2096
2097
2098 /***
2099  * DESCRIPTION:
2100  * Returns the current icon position, and advances it along the top.
2101  * The returned position is not offset by Origin.
2102  *
2103  * PARAMETER(S):
2104  * [I] infoPtr : valid pointer to the listview structure
2105  * [O] lpPos : will get the current icon position
2106  *
2107  * RETURN:
2108  * None
2109  */
2110 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2111 {
2112     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2113     
2114     *lpPos = infoPtr->currIconPos;
2115     
2116     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2117     if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2118
2119     infoPtr->currIconPos.x  = 0;
2120     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2121 }
2122
2123     
2124 /***
2125  * DESCRIPTION:
2126  * Returns the current icon position, and advances it down the left edge.
2127  * The returned position is not offset by Origin.
2128  *
2129  * PARAMETER(S):
2130  * [I] infoPtr : valid pointer to the listview structure
2131  * [O] lpPos : will get the current icon position
2132  *
2133  * RETURN:
2134  * None
2135  */
2136 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2137 {
2138     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2139     
2140     *lpPos = infoPtr->currIconPos;
2141     
2142     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2143     if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2144
2145     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2146     infoPtr->currIconPos.y  = 0;
2147 }
2148
2149     
2150 /***
2151  * DESCRIPTION:
2152  * Moves an icon to the specified position.
2153  * It takes care of invalidating the item, etc.
2154  *
2155  * PARAMETER(S):
2156  * [I] infoPtr : valid pointer to the listview structure
2157  * [I] nItem : the item to move
2158  * [I] lpPos : the new icon position
2159  * [I] isNew : flags the item as being new
2160  *
2161  * RETURN:
2162  *   Success: TRUE
2163  *   Failure: FALSE
2164  */
2165 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2166 {
2167     POINT old;
2168     
2169     if (!isNew)
2170     { 
2171         old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2172         old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2173     
2174         if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2175         LISTVIEW_InvalidateItem(infoPtr, nItem);
2176     }
2177
2178     /* Allocating a POINTER for every item is too resource intensive,
2179      * so we'll keep the (x,y) in different arrays */
2180     if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2181     if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2182
2183     LISTVIEW_InvalidateItem(infoPtr, nItem);
2184
2185     return TRUE;
2186 }
2187
2188 /***
2189  * DESCRIPTION:
2190  * Arranges listview items in icon display mode.
2191  *
2192  * PARAMETER(S):
2193  * [I] infoPtr : valid pointer to the listview structure
2194  * [I] nAlignCode : alignment code
2195  *
2196  * RETURN:
2197  *   SUCCESS : TRUE
2198  *   FAILURE : FALSE
2199  */
2200 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2201 {
2202     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2203     void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2204     POINT pos;
2205     INT i;
2206
2207     if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2208   
2209     TRACE("nAlignCode=%d\n", nAlignCode);
2210
2211     if (nAlignCode == LVA_DEFAULT)
2212     {
2213         if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2214         else nAlignCode = LVA_ALIGNTOP;
2215     }
2216    
2217     switch (nAlignCode)
2218     {
2219     case LVA_ALIGNLEFT:  next_pos = LISTVIEW_NextIconPosLeft; break;
2220     case LVA_ALIGNTOP:   next_pos = LISTVIEW_NextIconPosTop;  break;
2221     case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop;  break; /* FIXME */
2222     default: return FALSE;
2223     }
2224     
2225     infoPtr->bAutoarrange = TRUE;
2226     infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2227     for (i = 0; i < infoPtr->nItemCount; i++)
2228     {
2229         next_pos(infoPtr, &pos);
2230         LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2231     }
2232
2233     return TRUE;
2234 }
2235   
2236 /***
2237  * DESCRIPTION:
2238  * Retrieves the bounding rectangle of all the items, not offset by Origin.
2239  *
2240  * PARAMETER(S):
2241  * [I] infoPtr : valid pointer to the listview structure
2242  * [O] lprcView : bounding rectangle
2243  *
2244  * RETURN:
2245  *   SUCCESS : TRUE
2246  *   FAILURE : FALSE
2247  */
2248 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2249 {
2250     INT i, x, y;
2251
2252     SetRectEmpty(lprcView);
2253
2254     switch (infoPtr->dwStyle & LVS_TYPEMASK)
2255     {
2256     case LVS_ICON:
2257     case LVS_SMALLICON:
2258         for (i = 0; i < infoPtr->nItemCount; i++)
2259         {
2260             x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2261            y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2262             lprcView->right = max(lprcView->right, x);
2263             lprcView->bottom = max(lprcView->bottom, y);
2264         }
2265         if (infoPtr->nItemCount > 0)
2266         {
2267             lprcView->right += infoPtr->nItemWidth;
2268             lprcView->bottom += infoPtr->nItemHeight;
2269         }
2270         break;
2271
2272     case LVS_LIST:
2273         y = LISTVIEW_GetCountPerColumn(infoPtr);
2274         x = infoPtr->nItemCount / y;
2275         if (infoPtr->nItemCount % y) x++;
2276         lprcView->right = x * infoPtr->nItemWidth;
2277         lprcView->bottom = y * infoPtr->nItemHeight;
2278         break;
2279         
2280     case LVS_REPORT:        
2281         lprcView->right = infoPtr->nItemWidth;
2282         lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2283         break;
2284     }
2285 }
2286
2287 /***
2288  * DESCRIPTION:
2289  * Retrieves the bounding rectangle of all the items.
2290  *
2291  * PARAMETER(S):
2292  * [I] infoPtr : valid pointer to the listview structure
2293  * [O] lprcView : bounding rectangle
2294  *
2295  * RETURN:
2296  *   SUCCESS : TRUE
2297  *   FAILURE : FALSE
2298  */
2299 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2300 {
2301     POINT ptOrigin;
2302
2303     TRACE("(lprcView=%p)\n", lprcView);
2304
2305     if (!lprcView) return FALSE;
2306  
2307     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2308     LISTVIEW_GetAreaRect(infoPtr, lprcView); 
2309     OffsetRect(lprcView, ptOrigin.x, ptOrigin.y); 
2310
2311     TRACE("lprcView=%s\n", debugrect(lprcView));
2312
2313     return TRUE;
2314 }
2315
2316 /***
2317  * DESCRIPTION:
2318  * Retrieves the subitem pointer associated with the subitem index.
2319  *
2320  * PARAMETER(S):
2321  * [I] hdpaSubItems : DPA handle for a specific item
2322  * [I] nSubItem : index of subitem
2323  *
2324  * RETURN:
2325  *   SUCCESS : subitem pointer
2326  *   FAILURE : NULL
2327  */
2328 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2329 {
2330     SUBITEM_INFO *lpSubItem;
2331     INT i;
2332
2333     /* we should binary search here if need be */
2334     for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2335     {
2336         lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2337         if (lpSubItem->iSubItem == nSubItem)
2338             return lpSubItem;
2339     }
2340
2341     return NULL;
2342 }
2343
2344
2345 /***
2346  * DESCRIPTION:
2347  * Caclulates the desired item width.
2348  *
2349  * PARAMETER(S):
2350  * [I] infoPtr : valid pointer to the listview structure
2351  *
2352  * RETURN:
2353  *  The desired item width.
2354  */
2355 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2356 {
2357     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2358     INT nItemWidth = 0;
2359
2360     TRACE("uView=%d\n", uView);
2361
2362     if (uView == LVS_ICON)
2363         nItemWidth = infoPtr->iconSpacing.cx;
2364     else if (uView == LVS_REPORT)
2365     {
2366         RECT rcHeader;
2367
2368         if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2369         {
2370             LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2371             nItemWidth = rcHeader.right;
2372         }
2373     }
2374     else /* LVS_SMALLICON, or LVS_LIST */
2375     {
2376         INT i;
2377         
2378         for (i = 0; i < infoPtr->nItemCount; i++)
2379             nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2380
2381         if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx; 
2382         if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2383
2384         nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2385     }
2386
2387     return max(nItemWidth, 1);
2388 }
2389
2390 /***
2391  * DESCRIPTION:
2392  * Caclulates the desired item height.
2393  *
2394  * PARAMETER(S):
2395  * [I] infoPtr : valid pointer to the listview structure
2396  *
2397  * RETURN:
2398  *  The desired item height.
2399  */
2400 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2401 {
2402     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2403     INT nItemHeight;
2404
2405     TRACE("uView=%d\n", uView);
2406
2407     if (uView == LVS_ICON)
2408         nItemHeight = infoPtr->iconSpacing.cy;
2409     else
2410     {
2411         nItemHeight = infoPtr->ntmHeight; 
2412         if (infoPtr->himlState)
2413             nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2414         if (infoPtr->himlSmall)
2415             nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2416         if (infoPtr->himlState || infoPtr->himlSmall)
2417             nItemHeight += HEIGHT_PADDING;
2418     }
2419
2420     return max(nItemHeight, 1);
2421 }
2422
2423 /***
2424  * DESCRIPTION:
2425  * Updates the width, and height of an item.
2426  *
2427  * PARAMETER(S):
2428  * [I] infoPtr : valid pointer to the listview structure
2429  *
2430  * RETURN:
2431  *  None.
2432  */
2433 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2434 {
2435     infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2436     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2437 }
2438
2439
2440 /***
2441  * DESCRIPTION:
2442  * Retrieves and saves important text metrics info for the current
2443  * Listview font.
2444  *
2445  * PARAMETER(S):
2446  * [I] infoPtr : valid pointer to the listview structure
2447  *
2448  */
2449 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2450 {
2451     HDC hdc = GetDC(infoPtr->hwndSelf);
2452     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2453     HFONT hOldFont = SelectObject(hdc, hFont);
2454     TEXTMETRICW tm;
2455
2456     if (GetTextMetricsW(hdc, &tm))
2457     {
2458         infoPtr->ntmHeight = tm.tmHeight;
2459         infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2460     }
2461     SelectObject(hdc, hOldFont);
2462     ReleaseDC(infoPtr->hwndSelf, hdc);
2463     
2464     TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2465 }
2466
2467 /***
2468  * DESCRIPTION:
2469  * A compare function for ranges
2470  *
2471  * PARAMETER(S)
2472  * [I] range1 : pointer to range 1;
2473  * [I] range2 : pointer to range 2;
2474  * [I] flags : flags
2475  *
2476  * RETURNS:
2477  * > 0 : if range 1 > range 2
2478  * < 0 : if range 2 > range 1
2479  * = 0 : if range intersects range 2
2480  */
2481 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2482 {
2483     INT cmp;
2484     
2485     if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower) 
2486         cmp = -1;
2487     else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower) 
2488         cmp = 1;
2489     else 
2490         cmp = 0;
2491     
2492     TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2493
2494     return cmp;
2495 }
2496
2497 #if DEBUG_RANGES
2498 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2499 #else
2500 #define ranges_check(ranges, desc) do { } while(0)
2501 #endif
2502
2503 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2504 {
2505     INT i;
2506     RANGE *prev, *curr;
2507     
2508     TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2509     assert (ranges);
2510     assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2511     ranges_dump(ranges);
2512     prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2513     if (DPA_GetPtrCount(ranges->hdpa) > 0)
2514         assert (prev->lower >= 0 && prev->lower < prev->upper);
2515     for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2516     {
2517         curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2518         assert (prev->upper <= curr->lower);
2519         assert (curr->lower < curr->upper);
2520         prev = curr;
2521     }
2522     TRACE("--- Done checking---\n");
2523 }
2524
2525 static RANGES ranges_create(int count)
2526 {
2527     RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2528     if (!ranges) return NULL;
2529     ranges->hdpa = DPA_Create(count);
2530     if (ranges->hdpa) return ranges;
2531     Free(ranges);
2532     return NULL;
2533 }
2534
2535 static void ranges_clear(RANGES ranges)
2536 {
2537     INT i;
2538         
2539     for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2540         Free(DPA_GetPtr(ranges->hdpa, i));
2541     DPA_DeleteAllPtrs(ranges->hdpa);
2542 }
2543
2544
2545 static void ranges_destroy(RANGES ranges)
2546 {
2547     if (!ranges) return;
2548     ranges_clear(ranges);
2549     DPA_Destroy(ranges->hdpa);
2550     Free(ranges);
2551 }
2552
2553 static RANGES ranges_clone(RANGES ranges)
2554 {
2555     RANGES clone;
2556     INT i;
2557            
2558     if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2559
2560     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2561     {
2562         RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2563         if (!newrng) goto fail;
2564         *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2565         DPA_SetPtr(clone->hdpa, i, newrng);
2566     }
2567     return clone;
2568     
2569 fail:
2570     TRACE ("clone failed\n");
2571     ranges_destroy(clone);
2572     return NULL;
2573 }
2574
2575 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2576 {
2577     INT i;
2578
2579     for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2580         ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2581
2582     return ranges;
2583 }
2584
2585 static void ranges_dump(RANGES ranges)
2586 {
2587     INT i;
2588
2589     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2590         TRACE("   %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2591 }
2592
2593 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2594 {
2595     RANGE srchrng = { nItem, nItem + 1 };
2596
2597     TRACE("(nItem=%d)\n", nItem);
2598     ranges_check(ranges, "before contain");
2599     return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2600 }
2601
2602 static INT ranges_itemcount(RANGES ranges)
2603 {
2604     INT i, count = 0;
2605     
2606     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2607     {
2608         RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2609         count += sel->upper - sel->lower;
2610     }
2611
2612     return count;
2613 }
2614
2615 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2616 {
2617     RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2618     INT index;
2619
2620     index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2621     if (index == -1) return TRUE;
2622
2623     for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2624     {
2625         chkrng = DPA_GetPtr(ranges->hdpa, index);
2626         if (chkrng->lower >= nItem)
2627             chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2628         if (chkrng->upper > nItem)
2629             chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2630     }
2631     return TRUE;
2632 }
2633
2634 static BOOL ranges_add(RANGES ranges, RANGE range)
2635 {
2636     RANGE srchrgn;
2637     INT index;
2638
2639     TRACE("(%s)\n", debugrange(&range));
2640     ranges_check(ranges, "before add");
2641
2642     /* try find overlapping regions first */
2643     srchrgn.lower = range.lower - 1;
2644     srchrgn.upper = range.upper + 1;
2645     index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2646    
2647     if (index == -1)
2648     {
2649         RANGE *newrgn;
2650
2651         TRACE("Adding new range\n");
2652
2653         /* create the brand new range to insert */      
2654         newrgn = (RANGE *)Alloc(sizeof(RANGE));
2655         if(!newrgn) goto fail;
2656         *newrgn = range;
2657         
2658         /* figure out where to insert it */
2659         index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2660         TRACE("index=%d\n", index);
2661         if (index == -1) index = 0;
2662         
2663         /* and get it over with */
2664         if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2665         {
2666             Free(newrgn);
2667             goto fail;
2668         }
2669     }
2670     else
2671     {
2672         RANGE *chkrgn, *mrgrgn;
2673         INT fromindex, mergeindex;
2674
2675         chkrgn = DPA_GetPtr(ranges->hdpa, index);
2676         TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2677
2678         chkrgn->lower = min(range.lower, chkrgn->lower);
2679         chkrgn->upper = max(range.upper, chkrgn->upper);
2680         
2681         TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2682
2683         /* merge now common anges */
2684         fromindex = 0;
2685         srchrgn.lower = chkrgn->lower - 1;
2686         srchrgn.upper = chkrgn->upper + 1;
2687             
2688         do
2689         {
2690             mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2691             if (mergeindex == -1) break;
2692             if (mergeindex == index) 
2693             {
2694                 fromindex = index + 1;
2695                 continue;
2696             }
2697           
2698             TRACE("Merge with index %i\n", mergeindex);
2699             
2700             mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2701             chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2702             chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2703             Free(mrgrgn);
2704             DPA_DeletePtr(ranges->hdpa, mergeindex);
2705             if (mergeindex < index) index --;
2706         } while(1);
2707     }
2708
2709     ranges_check(ranges, "after add");
2710     return TRUE;
2711     
2712 fail:
2713     ranges_check(ranges, "failed add");
2714     return FALSE;
2715 }
2716
2717 static BOOL ranges_del(RANGES ranges, RANGE range)
2718 {
2719     RANGE *chkrgn;
2720     INT index;
2721
2722     TRACE("(%s)\n", debugrange(&range));
2723     ranges_check(ranges, "before del");
2724     
2725     /* we don't use DPAS_SORTED here, since we need *
2726      * to find the first overlapping range          */
2727     index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2728     while(index != -1) 
2729     {
2730         chkrgn = DPA_GetPtr(ranges->hdpa, index);
2731         
2732         TRACE("Matches range %s @%d\n", debugrange(chkrgn), index); 
2733
2734         /* case 1: Same range */
2735         if ( (chkrgn->upper == range.upper) &&
2736              (chkrgn->lower == range.lower) )
2737         {
2738             DPA_DeletePtr(ranges->hdpa, index);
2739             break;
2740         }
2741         /* case 2: engulf */
2742         else if ( (chkrgn->upper <= range.upper) &&
2743                   (chkrgn->lower >= range.lower) ) 
2744         {
2745             DPA_DeletePtr(ranges->hdpa, index);
2746         }
2747         /* case 3: overlap upper */
2748         else if ( (chkrgn->upper <= range.upper) &&
2749                   (chkrgn->lower < range.lower) )
2750         {
2751             chkrgn->upper = range.lower;
2752         }
2753         /* case 4: overlap lower */
2754         else if ( (chkrgn->upper > range.upper) &&
2755                   (chkrgn->lower >= range.lower) )
2756         {
2757             chkrgn->lower = range.upper;
2758             break;
2759         }
2760         /* case 5: fully internal */
2761         else
2762         {
2763             RANGE tmprgn = *chkrgn, *newrgn;
2764
2765             if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2766             newrgn->lower = chkrgn->lower;
2767             newrgn->upper = range.lower;
2768             chkrgn->lower = range.upper;
2769             if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2770             {
2771                 Free(newrgn);
2772                 goto fail;
2773             }
2774             chkrgn = &tmprgn;
2775             break;
2776         }
2777
2778         index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2779     }
2780
2781     ranges_check(ranges, "after del");
2782     return TRUE;
2783
2784 fail:
2785     ranges_check(ranges, "failed del");
2786     return FALSE;
2787 }
2788
2789 /***
2790 * DESCRIPTION:
2791 * Removes all selection ranges
2792 *
2793 * Parameters(s):
2794 * [I] infoPtr : valid pointer to the listview structure
2795 * [I] toSkip : item range to skip removing the selection
2796 *
2797 * RETURNS:
2798 *   SUCCESS : TRUE
2799 *   FAILURE : TRUE
2800 */
2801 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2802 {
2803     LVITEMW lvItem;
2804     ITERATOR i;
2805     RANGES clone;
2806
2807     TRACE("()\n");
2808
2809     lvItem.state = 0;
2810     lvItem.stateMask = LVIS_SELECTED;
2811     
2812     /* need to clone the DPA because callbacks can change it */
2813     if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2814     iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2815     while(iterator_next(&i))
2816         LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2817     /* note that the iterator destructor will free the cloned range */
2818     iterator_destroy(&i);
2819
2820     return TRUE;
2821 }
2822
2823 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2824 {
2825     RANGES toSkip;
2826    
2827     if (!(toSkip = ranges_create(1))) return FALSE;
2828     if (nItem != -1) ranges_additem(toSkip, nItem);
2829     LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2830     ranges_destroy(toSkip);
2831     return TRUE;
2832 }
2833
2834 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2835 {
2836     return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2837 }
2838
2839 /***
2840  * DESCRIPTION:
2841  * Retrieves the number of items that are marked as selected.
2842  *
2843  * PARAMETER(S):
2844  * [I] infoPtr : valid pointer to the listview structure
2845  *
2846  * RETURN:
2847  * Number of items selected.
2848  */
2849 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2850 {
2851     INT nSelectedCount = 0;
2852
2853     if (infoPtr->uCallbackMask & LVIS_SELECTED)
2854     {
2855         INT i;
2856         for (i = 0; i < infoPtr->nItemCount; i++)
2857         {
2858             if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2859                 nSelectedCount++;
2860         }
2861     }
2862     else
2863         nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2864
2865     TRACE("nSelectedCount=%d\n", nSelectedCount);
2866     return nSelectedCount;
2867 }
2868
2869 /***
2870  * DESCRIPTION:
2871  * Manages the item focus.
2872  *
2873  * PARAMETER(S):
2874  * [I] infoPtr : valid pointer to the listview structure
2875  * [I] nItem : item index
2876  *
2877  * RETURN:
2878  *   TRUE : focused item changed
2879  *   FALSE : focused item has NOT changed
2880  */
2881 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2882 {
2883     INT oldFocus = infoPtr->nFocusedItem;
2884     LVITEMW lvItem;
2885
2886     if (nItem == infoPtr->nFocusedItem) return FALSE;
2887     
2888     lvItem.state =  nItem == -1 ? 0 : LVIS_FOCUSED;
2889     lvItem.stateMask = LVIS_FOCUSED;
2890     LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2891
2892     return oldFocus != infoPtr->nFocusedItem;
2893 }
2894
2895 /* Helper function for LISTVIEW_ShiftIndices *only* */
2896 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2897 {
2898     if (nShiftItem < nItem) return nShiftItem;
2899
2900     if (nShiftItem > nItem) return nShiftItem + direction;
2901
2902     if (direction > 0) return nShiftItem + direction;
2903
2904     return min(nShiftItem, infoPtr->nItemCount - 1);
2905 }
2906
2907 /**
2908 * DESCRIPTION:
2909 * Updates the various indices after an item has been inserted or deleted.
2910 *
2911 * PARAMETER(S):
2912 * [I] infoPtr : valid pointer to the listview structure
2913 * [I] nItem : item index
2914 * [I] direction : Direction of shift, +1 or -1.
2915 *
2916 * RETURN:
2917 * None
2918 */
2919 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2920 {
2921     INT nNewFocus;
2922     BOOL bOldChange;
2923
2924     /* temporarily disable change notification while shifting items */
2925     bOldChange = infoPtr->bDoChangeNotify;
2926     infoPtr->bDoChangeNotify = FALSE;
2927
2928     TRACE("Shifting %iu, %i steps\n", nItem, direction);
2929
2930     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2931
2932     assert(abs(direction) == 1);
2933
2934     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2935
2936     nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2937     if (nNewFocus != infoPtr->nFocusedItem)
2938         LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2939     
2940     /* But we are not supposed to modify nHotItem! */
2941
2942     infoPtr->bDoChangeNotify = bOldChange;
2943 }
2944
2945
2946 /**
2947  * DESCRIPTION:
2948  * Adds a block of selections.
2949  *
2950  * PARAMETER(S):
2951  * [I] infoPtr : valid pointer to the listview structure
2952  * [I] nItem : item index
2953  *
2954  * RETURN:
2955  * None
2956  */
2957 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2958 {
2959     INT nFirst = min(infoPtr->nSelectionMark, nItem);
2960     INT nLast = max(infoPtr->nSelectionMark, nItem);
2961     INT i;
2962     LVITEMW item;
2963
2964     if (nFirst == -1) nFirst = nItem;
2965
2966     item.state = LVIS_SELECTED;
2967     item.stateMask = LVIS_SELECTED;
2968
2969     /* FIXME: this is not correct LVS_OWNERDATA
2970      * setting the item states individually will generate
2971      * a LVN_ITEMCHANGED notification for each one. Instead,
2972      * we have to send a LVN_ODSTATECHANGED notification.
2973      * See MSDN documentation for LVN_ITEMCHANGED.
2974      */
2975     for (i = nFirst; i <= nLast; i++)
2976         LISTVIEW_SetItemState(infoPtr,i,&item);
2977 }
2978
2979
2980 /***
2981  * DESCRIPTION:
2982  * Sets a single group selection.
2983  *
2984  * PARAMETER(S):
2985  * [I] infoPtr : valid pointer to the listview structure
2986  * [I] nItem : item index
2987  *
2988  * RETURN:
2989  * None
2990  */
2991 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2992 {
2993     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2994     RANGES selection;
2995     LVITEMW item;
2996     ITERATOR i;
2997
2998     if (!(selection = ranges_create(100))) return;
2999
3000     item.state = LVIS_SELECTED; 
3001     item.stateMask = LVIS_SELECTED;
3002
3003     if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3004     {
3005         if (infoPtr->nSelectionMark == -1)
3006         {
3007             infoPtr->nSelectionMark = nItem;
3008             ranges_additem(selection, nItem);
3009         }
3010         else
3011         {
3012             RANGE sel;
3013             
3014             sel.lower = min(infoPtr->nSelectionMark, nItem);
3015             sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3016             ranges_add(selection, sel);
3017         }
3018     }
3019     else
3020     {
3021         RECT rcItem, rcSel, rcSelMark;
3022         POINT ptItem;
3023         
3024         rcItem.left = LVIR_BOUNDS;
3025         if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3026         rcSelMark.left = LVIR_BOUNDS;
3027         if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3028         UnionRect(&rcSel, &rcItem, &rcSelMark);
3029         iterator_frameditems(&i, infoPtr, &rcSel);
3030         while(iterator_next(&i))
3031         {
3032             LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3033             if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3034         }
3035         iterator_destroy(&i);
3036     }
3037
3038     LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3039     iterator_rangesitems(&i, selection);
3040     while(iterator_next(&i))
3041         LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3042     /* this will also destroy the selection */
3043     iterator_destroy(&i);
3044     
3045     LISTVIEW_SetItemFocus(infoPtr, nItem);
3046 }
3047
3048 /***
3049  * DESCRIPTION:
3050  * Sets a single selection.
3051  *
3052  * PARAMETER(S):
3053  * [I] infoPtr : valid pointer to the listview structure
3054  * [I] nItem : item index
3055  *
3056  * RETURN:
3057  * None
3058  */
3059 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3060 {
3061     LVITEMW lvItem;
3062
3063     TRACE("nItem=%d\n", nItem);
3064     
3065     LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3066
3067     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3068     lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3069     LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3070
3071     infoPtr->nSelectionMark = nItem;
3072 }
3073
3074 /***
3075  * DESCRIPTION:
3076  * Set selection(s) with keyboard.
3077  *
3078  * PARAMETER(S):
3079  * [I] infoPtr : valid pointer to the listview structure
3080  * [I] nItem : item index
3081  *
3082  * RETURN:
3083  *   SUCCESS : TRUE (needs to be repainted)
3084  *   FAILURE : FALSE (nothing has changed)
3085  */
3086 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3087 {
3088   /* FIXME: pass in the state */
3089   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3090   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3091   BOOL bResult = FALSE;
3092
3093   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3094   {
3095     if (infoPtr->dwStyle & LVS_SINGLESEL)
3096     {
3097       bResult = TRUE;
3098       LISTVIEW_SetSelection(infoPtr, nItem);
3099     }
3100     else
3101     {
3102       if (wShift)
3103       {
3104         bResult = TRUE;
3105         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3106       }
3107       else if (wCtrl)
3108       {
3109         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3110       }
3111       else
3112       {
3113         bResult = TRUE;
3114         LISTVIEW_SetSelection(infoPtr, nItem);
3115       }
3116     }
3117     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3118   }
3119
3120   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3121   return bResult;
3122 }
3123
3124
3125 /***
3126  * DESCRIPTION:
3127  * Called when the mouse is being actively tracked and has hovered for a specified
3128  * amount of time
3129  *
3130  * PARAMETER(S):
3131  * [I] infoPtr : valid pointer to the listview structure
3132  * [I] fwKeys : key indicator
3133  * [I] pts : mouse position
3134  *
3135  * RETURN:
3136  *   0 if the message was processed, non-zero if there was an error
3137  *
3138  * INFO:
3139  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3140  * over the item for a certain period of time.
3141  *
3142  */
3143 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3144 {
3145     if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3146         /* FIXME: select the item!!! */
3147         /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3148
3149     return 0;
3150 }
3151
3152 /***
3153  * DESCRIPTION:
3154  * Called whenever WM_MOUSEMOVE is received.
3155  *
3156  * PARAMETER(S):
3157  * [I] infoPtr : valid pointer to the listview structure
3158  * [I] fwKeys : key indicator
3159  * [I] pts : mouse position
3160  *
3161  * RETURN:
3162  *   0 if the message is processed, non-zero if there was an error
3163  */
3164 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3165 {
3166   TRACKMOUSEEVENT trackinfo;
3167
3168   /* see if we are supposed to be tracking mouse hovering */
3169   if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3170      /* fill in the trackinfo struct */
3171      trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3172      trackinfo.dwFlags = TME_QUERY;
3173      trackinfo.hwndTrack = infoPtr->hwndSelf;
3174      trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3175
3176      /* see if we are already tracking this hwnd */
3177      _TrackMouseEvent(&trackinfo);
3178
3179      if(!(trackinfo.dwFlags & TME_HOVER)) {
3180        trackinfo.dwFlags = TME_HOVER;
3181
3182        /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3183        _TrackMouseEvent(&trackinfo);
3184     }
3185   }
3186
3187   return 0;
3188 }
3189
3190
3191 /***
3192  * Tests wheather the item is assignable to a list with style lStyle 
3193  */
3194 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3195 {
3196     if ( (lpLVItem->mask & LVIF_TEXT) && 
3197         (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3198         (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3199     
3200     return TRUE;
3201 }
3202
3203
3204 /***
3205  * DESCRIPTION:
3206  * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3207  *
3208  * PARAMETER(S):
3209  * [I] infoPtr : valid pointer to the listview structure
3210  * [I] lpLVItem : valid pointer to new item atttributes
3211  * [I] isNew : the item being set is being inserted
3212  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3213  * [O] bChanged : will be set to TRUE if the item really changed
3214  *
3215  * RETURN:
3216  *   SUCCESS : TRUE
3217  *   FAILURE : FALSE
3218  */
3219 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3220 {
3221     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3222     ITEM_INFO *lpItem;
3223     NMLISTVIEW nmlv;
3224     UINT uChanged = 0;
3225     LVITEMW item;
3226
3227     TRACE("()\n");
3228
3229     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3230     
3231     if (lpLVItem->mask == 0) return TRUE;   
3232
3233     if (infoPtr->dwStyle & LVS_OWNERDATA)
3234     {
3235         /* a virtual listview we stores only selection and focus */
3236         if (lpLVItem->mask & ~LVIF_STATE)
3237             return FALSE;
3238         lpItem = NULL;
3239     }
3240     else
3241     {
3242         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3243         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3244         assert (lpItem);
3245     }
3246
3247     /* we need to get the lParam and state of the item */
3248     item.iItem = lpLVItem->iItem;
3249     item.iSubItem = lpLVItem->iSubItem;
3250     item.mask = LVIF_STATE | LVIF_PARAM;
3251     item.stateMask = ~0;
3252     item.state = 0;
3253     item.lParam = 0;
3254     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3255
3256     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3257     /* determine what fields will change */    
3258     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3259         uChanged |= LVIF_STATE;
3260
3261     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3262         uChanged |= LVIF_IMAGE;
3263
3264     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3265         uChanged |= LVIF_PARAM;
3266
3267     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3268         uChanged |= LVIF_INDENT;
3269
3270     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3271         uChanged |= LVIF_TEXT;
3272    
3273     TRACE("uChanged=0x%x\n", uChanged); 
3274     if (!uChanged) return TRUE;
3275     *bChanged = TRUE;
3276     
3277     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3278     nmlv.iItem = lpLVItem->iItem;
3279     nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3280     nmlv.uOldState = item.state;
3281     nmlv.uChanged = uChanged;
3282     nmlv.lParam = item.lParam;
3283     
3284     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3285     /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3286     /* are enabled */
3287     if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3288        notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3289         return FALSE;
3290
3291     /* copy information */
3292     if (lpLVItem->mask & LVIF_TEXT)
3293         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3294
3295     if (lpLVItem->mask & LVIF_IMAGE)
3296         lpItem->hdr.iImage = lpLVItem->iImage;
3297
3298     if (lpLVItem->mask & LVIF_PARAM)
3299         lpItem->lParam = lpLVItem->lParam;
3300
3301     if (lpLVItem->mask & LVIF_INDENT)
3302         lpItem->iIndent = lpLVItem->iIndent;
3303
3304     if (uChanged & LVIF_STATE)
3305     {
3306         if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3307         {
3308             lpItem->state &= ~lpLVItem->stateMask;
3309             lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3310         }
3311         if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3312         {
3313             if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3314             ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3315         }
3316         else if (lpLVItem->stateMask & LVIS_SELECTED)
3317             ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3318         
3319         /* if we are asked to change focus, and we manage it, do it */
3320         if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3321         {
3322             if (lpLVItem->state & LVIS_FOCUSED)
3323             {
3324                 LISTVIEW_SetItemFocus(infoPtr, -1);
3325                 infoPtr->nFocusedItem = lpLVItem->iItem;
3326                 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3327             }
3328             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3329                 infoPtr->nFocusedItem = -1;
3330         }
3331     }
3332
3333     /* if we're inserting the item, we're done */
3334     if (isNew) return TRUE;
3335     
3336     /* send LVN_ITEMCHANGED notification */
3337     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3338     if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3339
3340     return TRUE;
3341 }
3342
3343 /***
3344  * DESCRIPTION:
3345  * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3346  *
3347  * PARAMETER(S):
3348  * [I] infoPtr : valid pointer to the listview structure
3349  * [I] lpLVItem : valid pointer to new subitem atttributes
3350  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3351  * [O] bChanged : will be set to TRUE if the item really changed
3352  *
3353  * RETURN:
3354  *   SUCCESS : TRUE
3355  *   FAILURE : FALSE
3356  */
3357 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3358 {
3359     HDPA hdpaSubItems;
3360     SUBITEM_INFO *lpSubItem;
3361
3362     /* we do not support subitems for virtual listviews */
3363     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3364     
3365     /* set subitem only if column is present */
3366     if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3367    
3368     /* First do some sanity checks */
3369     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3370     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3371    
3372     /* get the subitem structure, and create it if not there */
3373     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3374     assert (hdpaSubItems);
3375     
3376     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3377     if (!lpSubItem)
3378     {
3379         SUBITEM_INFO *tmpSubItem;
3380         INT i;
3381
3382         lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3383         if (!lpSubItem) return FALSE;
3384         /* we could binary search here, if need be...*/
3385         for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3386         {
3387             tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3388             if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3389         }
3390         if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3391         {
3392             Free(lpSubItem);
3393             return FALSE;
3394         }
3395         lpSubItem->iSubItem = lpLVItem->iSubItem;
3396         lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3397         *bChanged = TRUE;
3398     }
3399     
3400     if (lpLVItem->mask & LVIF_IMAGE)
3401         if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3402         {
3403             lpSubItem->hdr.iImage = lpLVItem->iImage;
3404             *bChanged = TRUE;
3405         }
3406
3407     if (lpLVItem->mask & LVIF_TEXT)
3408         if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3409         {
3410             textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3411             *bChanged = TRUE;
3412         }
3413
3414     return TRUE;
3415 }
3416
3417 /***
3418  * DESCRIPTION:
3419  * Sets item attributes.
3420  *
3421  * PARAMETER(S):
3422  * [I] infoPtr : valid pointer to the listview structure
3423  * [I] lpLVItem : new item atttributes
3424  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3425  *
3426  * RETURN:
3427  *   SUCCESS : TRUE
3428  *   FAILURE : FALSE
3429  */
3430 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3431 {
3432     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3433     LPWSTR pszText = NULL;
3434     BOOL bResult, bChanged = FALSE;
3435     
3436     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3437
3438     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3439         return FALSE;
3440
3441     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3442     if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3443     {
3444         pszText = lpLVItem->pszText;
3445         ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3446     }
3447     
3448     /* actually set the fields */
3449     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3450     
3451     if (lpLVItem->iSubItem)
3452         bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3453     else
3454         bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3455
3456     /* redraw item, if necessary */
3457     if (bChanged && !infoPtr->bIsDrawing)
3458     {
3459         /* this little optimization eliminates some nasty flicker */
3460         if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3461              (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3462             LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3463         else
3464             LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3465     }
3466     /* restore text */
3467     if (pszText)
3468     {
3469         textfreeT(lpLVItem->pszText, isW);
3470         ((LVITEMW *)lpLVItem)->pszText = pszText;
3471     }
3472
3473     return bResult;
3474 }
3475
3476 /***
3477  * DESCRIPTION:
3478  * Retrieves the index of the item at coordinate (0, 0) of the client area.
3479  *
3480  * PARAMETER(S):
3481  * [I] infoPtr : valid pointer to the listview structure
3482  *
3483  * RETURN:
3484  * item index
3485  */
3486 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3487 {
3488     LONG lStyle = infoPtr->dwStyle;
3489     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3490     INT nItem = 0;
3491     SCROLLINFO scrollInfo;
3492
3493     scrollInfo.cbSize = sizeof(SCROLLINFO);
3494     scrollInfo.fMask = SIF_POS;
3495
3496     if (uView == LVS_LIST)
3497     {
3498         if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3499             nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3500     }
3501     else if (uView == LVS_REPORT)
3502     {
3503         if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3504             nItem = scrollInfo.nPos;
3505     } 
3506     else
3507     {
3508         if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3509             nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3510     }
3511
3512     TRACE("nItem=%d\n", nItem);
3513     
3514     return nItem;
3515 }
3516
3517
3518 /***
3519  * DESCRIPTION:
3520  * Erases the background of the given rectangle
3521  *
3522  * PARAMETER(S):
3523  * [I] infoPtr : valid pointer to the listview structure
3524  * [I] hdc : device context handle
3525  * [I] lprcBox : clipping rectangle
3526  *
3527  * RETURN:
3528  *   Success: TRUE
3529  *   Failure: FALSE
3530  */
3531 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3532 {
3533     if (!infoPtr->hBkBrush) return FALSE;
3534
3535     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3536
3537     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3538 }
3539
3540 /***
3541  * DESCRIPTION:
3542  * Draws an item.
3543  *
3544  * PARAMETER(S):
3545  * [I] infoPtr : valid pointer to the listview structure
3546  * [I] hdc : device context handle
3547  * [I] nItem : item index
3548  * [I] nSubItem : subitem index
3549  * [I] pos : item position in client coordinates
3550  * [I] cdmode : custom draw mode
3551  *
3552  * RETURN:
3553  *   Success: TRUE
3554  *   Failure: FALSE
3555  */
3556 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3557 {
3558     UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3559     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3560     static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3561     DWORD cdsubitemmode = CDRF_DODEFAULT;
3562     RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3563     NMLVCUSTOMDRAW nmlvcd;
3564     HIMAGELIST himl;
3565     LVITEMW lvItem;
3566
3567     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3568
3569     /* get information needed for drawing the item */
3570     lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3571     if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3572     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3573     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3574     lvItem.iItem = nItem;
3575     lvItem.iSubItem = nSubItem;
3576     lvItem.state = 0;
3577     lvItem.lParam = 0;
3578     lvItem.cchTextMax = DISP_TEXT_SIZE;
3579     lvItem.pszText = szDispText;
3580     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3581     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
3582         lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3583     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3584     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3585
3586     /* now check if we need to update the focus rectangle */
3587     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3588     
3589     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3590     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3591     OffsetRect(&rcBox, pos.x, pos.y);
3592     OffsetRect(&rcState, pos.x, pos.y);
3593     OffsetRect(&rcIcon, pos.x, pos.y);
3594     OffsetRect(&rcLabel, pos.x, pos.y);
3595     TRACE("    rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n", 
3596         debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3597
3598     /* fill in the custom draw structure */
3599     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3600
3601     if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3602     if (cdmode & CDRF_NOTIFYITEMDRAW)
3603         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3604     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3605     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3606     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3607     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3608     {
3609         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3610         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3611     }
3612     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3613         prepaint_setup(infoPtr, hdc, &nmlvcd);
3614
3615     /* in full row select, subitems, will just use main item's colors */
3616     if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3617         nmlvcd.clrTextBk = CLR_NONE;
3618     
3619     /* state icons */
3620     if (infoPtr->himlState && !IsRectEmpty(&rcState))
3621     {
3622         UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3623         if (uStateImage)
3624         {
3625              TRACE("uStateImage=%d\n", uStateImage);
3626              ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3627         }
3628     }
3629
3630     /* small icons */
3631     himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3632     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3633     {
3634         TRACE("iImage=%d\n", lvItem.iImage);
3635         ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3636                         (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3637     }
3638
3639     /* Don't bother painting item being edited */
3640     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3641
3642     /* draw the selection background, if we're drawing the main item */
3643     if (nSubItem == 0)
3644     {
3645         rcSelect = rcLabel;
3646         if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3647             rcSelect.right = rcBox.right;
3648    
3649         if (nmlvcd.clrTextBk != CLR_NONE) 
3650             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3651         if(lprcFocus) *lprcFocus = rcSelect;
3652     }
3653    
3654     /* figure out the text drawing flags */
3655     uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3656     if (uView == LVS_ICON)
3657         uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3658     else if (nSubItem)
3659     {
3660         switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3661         {
3662         case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
3663         case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3664         default:            uFormat |= DT_LEFT;
3665         }
3666     }
3667     if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3668     {
3669         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3670         else rcLabel.left += LABEL_HOR_PADDING;
3671     }
3672     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3673     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3674
3675 postpaint:
3676     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3677         notify_postpaint(infoPtr, &nmlvcd);
3678     return TRUE;
3679 }
3680
3681 /***
3682  * DESCRIPTION:
3683  * Draws listview items when in owner draw mode.
3684  *
3685  * PARAMETER(S):
3686  * [I] infoPtr : valid pointer to the listview structure
3687  * [I] hdc : device context handle
3688  *
3689  * RETURN:
3690  * None
3691  */
3692 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3693 {
3694     UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3695     DWORD cditemmode = CDRF_DODEFAULT;
3696     NMLVCUSTOMDRAW nmlvcd;
3697     POINT Origin, Position;
3698     DRAWITEMSTRUCT dis;
3699     LVITEMW item;
3700     
3701     TRACE("()\n");
3702
3703     ZeroMemory(&dis, sizeof(dis));
3704     
3705     /* Get scroll info once before loop */
3706     LISTVIEW_GetOrigin(infoPtr, &Origin);
3707     
3708     /* iterate through the invalidated rows */
3709     while(iterator_next(i))
3710     {
3711         item.iItem = i->nItem;
3712         item.iSubItem = 0;
3713         item.mask = LVIF_PARAM | LVIF_STATE;
3714         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3715         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3716            
3717         dis.CtlType = ODT_LISTVIEW;
3718         dis.CtlID = uID;
3719         dis.itemID = item.iItem;
3720         dis.itemAction = ODA_DRAWENTIRE;
3721         dis.itemState = 0;
3722         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3723         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3724         dis.hwndItem = infoPtr->hwndSelf;
3725         dis.hDC = hdc;
3726         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3727         dis.rcItem.left = Position.x + Origin.x;
3728         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3729         dis.rcItem.top = Position.y + Origin.y;
3730         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3731         dis.itemData = item.lParam;
3732
3733         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3734
3735     /*
3736      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3737      * structure for the rest. of the paint cycle
3738      */
3739         customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3740         if (cdmode & CDRF_NOTIFYITEMDRAW)
3741             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3742     
3743         if (!(cditemmode & CDRF_SKIPDEFAULT))
3744         {
3745             prepaint_setup (infoPtr, hdc, &nmlvcd);
3746             SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3747         }
3748
3749         if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3750             notify_postpaint(infoPtr, &nmlvcd);
3751     }
3752 }
3753
3754 /***
3755  * DESCRIPTION:
3756  * Draws listview items when in report display mode.
3757  *
3758  * PARAMETER(S):
3759  * [I] infoPtr : valid pointer to the listview structure
3760  * [I] hdc : device context handle
3761  * [I] cdmode : custom draw mode
3762  *
3763  * RETURN:
3764  * None
3765  */
3766 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3767 {
3768     INT rgntype;
3769     RECT rcClip, rcItem;
3770     POINT Origin, Position;
3771     RANGE colRange;
3772     ITERATOR j;
3773
3774     TRACE("()\n");
3775
3776     /* figure out what to draw */
3777     rgntype = GetClipBox(hdc, &rcClip);
3778     if (rgntype == NULLREGION) return;
3779     
3780     /* Get scroll info once before loop */
3781     LISTVIEW_GetOrigin(infoPtr, &Origin);
3782     
3783     /* narrow down the columns we need to paint */
3784     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3785     {
3786         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3787         if (rcItem.right + Origin.x >= rcClip.left) break;
3788     }
3789     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3790     {
3791         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3792         if (rcItem.left + Origin.x < rcClip.right) break;
3793     }
3794     iterator_rangeitems(&j, colRange);
3795
3796     /* in full row select, we _have_ to draw the main item */
3797     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3798         j.nSpecial = 0;
3799
3800     /* iterate through the invalidated rows */
3801     while(iterator_next(i))
3802     {
3803         /* iterate through the invalidated columns */
3804         while(iterator_next(&j))
3805         {
3806             LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3807             Position.x += Origin.x;
3808             Position.y += Origin.y;
3809
3810             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3811             {
3812                 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3813                 rcItem.top = 0;
3814                 rcItem.bottom = infoPtr->nItemHeight;
3815                 OffsetRect(&rcItem, Position.x, Position.y);
3816                 if (!RectVisible(hdc, &rcItem)) continue;
3817             }
3818
3819             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3820         }
3821     }
3822     iterator_destroy(&j);
3823 }
3824
3825 /***
3826  * DESCRIPTION:
3827  * Draws listview items when in list display mode.
3828  *
3829  * PARAMETER(S):
3830  * [I] infoPtr : valid pointer to the listview structure
3831  * [I] hdc : device context handle
3832  * [I] cdmode : custom draw mode
3833  *
3834  * RETURN:
3835  * None
3836  */
3837 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3838 {
3839     POINT Origin, Position;
3840
3841     /* Get scroll info once before loop */
3842     LISTVIEW_GetOrigin(infoPtr, &Origin);
3843     
3844     while(iterator_prev(i))
3845     {
3846         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3847         Position.x += Origin.x;
3848         Position.y += Origin.y;
3849
3850         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3851     }
3852 }
3853
3854
3855 /***
3856  * DESCRIPTION:
3857  * Draws listview items.
3858  *
3859  * PARAMETER(S):
3860  * [I] infoPtr : valid pointer to the listview structure
3861  * [I] hdc : device context handle
3862  *
3863  * RETURN:
3864  * NoneX
3865  */
3866 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3867 {
3868     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3869     COLORREF oldTextColor, oldClrTextBk, oldClrText;
3870     NMLVCUSTOMDRAW nmlvcd;
3871     HFONT hOldFont;
3872     DWORD cdmode;
3873     INT oldBkMode;
3874     RECT rcClient;
3875     ITERATOR i;
3876
3877     LISTVIEW_DUMP(infoPtr);
3878   
3879     infoPtr->bIsDrawing = TRUE;
3880
3881     /* save dc values we're gonna trash while drawing */
3882     hOldFont = SelectObject(hdc, infoPtr->hFont);
3883     oldBkMode = GetBkMode(hdc);
3884     infoPtr->clrTextBkDefault = GetBkColor(hdc);
3885     oldTextColor = GetTextColor(hdc);
3886
3887     oldClrTextBk = infoPtr->clrTextBk;
3888     oldClrText   = infoPtr->clrText;
3889    
3890     infoPtr->cditemmode = CDRF_DODEFAULT;
3891
3892     GetClientRect(infoPtr->hwndSelf, &rcClient);
3893     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3894     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3895     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3896     prepaint_setup(infoPtr, hdc, &nmlvcd);
3897
3898     /* Use these colors to draw the items */
3899     infoPtr->clrTextBk = nmlvcd.clrTextBk;
3900     infoPtr->clrText = nmlvcd.clrText;
3901
3902     /* nothing to draw */
3903     if(infoPtr->nItemCount == 0) goto enddraw;
3904
3905     /* figure out what we need to draw */
3906     iterator_visibleitems(&i, infoPtr, hdc);
3907     
3908     /* send cache hint notification */
3909     if (infoPtr->dwStyle & LVS_OWNERDATA)
3910     {
3911         RANGE range = iterator_range(&i);
3912         NMLVCACHEHINT nmlv;
3913         
3914         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3915         nmlv.iFrom = range.lower;
3916         nmlv.iTo   = range.upper - 1;
3917         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3918     }
3919
3920     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3921         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3922     else
3923     {
3924         if (uView == LVS_REPORT)
3925             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3926         else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3927             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3928
3929         /* if we have a focus rect, draw it */
3930         if (infoPtr->bFocus)
3931             DrawFocusRect(hdc, &infoPtr->rcFocus);
3932     }
3933     iterator_destroy(&i);
3934     
3935 enddraw:
3936     if (cdmode & CDRF_NOTIFYPOSTPAINT)
3937         notify_postpaint(infoPtr, &nmlvcd);
3938
3939     infoPtr->clrTextBk = oldClrTextBk;
3940     infoPtr->clrText = oldClrText;
3941
3942     SelectObject(hdc, hOldFont);
3943     SetBkMode(hdc, oldBkMode);
3944     SetBkColor(hdc, infoPtr->clrTextBkDefault);
3945     SetTextColor(hdc, oldTextColor);
3946     infoPtr->bIsDrawing = FALSE;
3947 }
3948
3949
3950 /***
3951  * DESCRIPTION:
3952  * Calculates the approximate width and height of a given number of items.
3953  *
3954  * PARAMETER(S):
3955  * [I] infoPtr : valid pointer to the listview structure
3956  * [I] nItemCount : number of items
3957  * [I] wWidth : width
3958  * [I] wHeight : height
3959  *
3960  * RETURN:
3961  * Returns a DWORD. The width in the low word and the height in high word.
3962  */
3963 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3964                                             WORD wWidth, WORD wHeight)
3965 {
3966   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3967   INT nItemCountPerColumn = 1;
3968   INT nColumnCount = 0;
3969   DWORD dwViewRect = 0;
3970
3971   if (nItemCount == -1)
3972     nItemCount = infoPtr->nItemCount;
3973
3974   if (uView == LVS_LIST)
3975   {
3976     if (wHeight == 0xFFFF)
3977     {
3978       /* use current height */
3979       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3980     }
3981
3982     if (wHeight < infoPtr->nItemHeight)
3983       wHeight = infoPtr->nItemHeight;
3984
3985     if (nItemCount > 0)
3986     {
3987       if (infoPtr->nItemHeight > 0)
3988       {
3989         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3990         if (nItemCountPerColumn == 0)
3991           nItemCountPerColumn = 1;
3992
3993         if (nItemCount % nItemCountPerColumn != 0)
3994           nColumnCount = nItemCount / nItemCountPerColumn;
3995         else
3996           nColumnCount = nItemCount / nItemCountPerColumn + 1;
3997       }
3998     }
3999
4000     /* Microsoft padding magic */
4001     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4002     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4003
4004     dwViewRect = MAKELONG(wWidth, wHeight);
4005   }
4006   else if (uView == LVS_REPORT)
4007   {
4008     RECT rcBox;
4009
4010     if (infoPtr->nItemCount > 0)
4011     {
4012       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4013       wWidth = rcBox.right - rcBox.left;
4014       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4015     }
4016     else
4017     {
4018       /* use current height and width */
4019       if (wHeight == 0xffff)
4020           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4021       if (wWidth == 0xffff)
4022           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4023     }
4024
4025     dwViewRect = MAKELONG(wWidth, wHeight);
4026   }
4027   else if (uView == LVS_SMALLICON)
4028     FIXME("uView == LVS_SMALLICON: not implemented\n");
4029   else if (uView == LVS_ICON)
4030     FIXME("uView == LVS_ICON: not implemented\n");
4031
4032   return dwViewRect;
4033 }
4034
4035
4036 /***
4037  * DESCRIPTION:
4038  * Create a drag image list for the specified item.
4039  *
4040  * PARAMETER(S):
4041  * [I] infoPtr : valid pointer to the listview structure
4042  * [I] iItem   : index of item
4043  * [O] lppt    : Upperr-left corner of the image
4044  *
4045  * RETURN:
4046  * Returns a handle to the image list if successful, NULL otherwise.
4047  */
4048 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4049 {
4050     RECT rcItem;
4051     SIZE size;
4052     POINT pos;
4053     HDC hdc, hdcOrig;
4054     HBITMAP hbmp, hOldbmp;
4055     HIMAGELIST dragList = 0;
4056     TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4057
4058     if (iItem < 0 || iItem >= infoPtr->nItemCount)
4059         return 0;
4060
4061     rcItem.left = LVIR_BOUNDS;
4062     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4063         return 0;
4064
4065     lppt->x = rcItem.left;
4066     lppt->y = rcItem.top;
4067
4068     size.cx = rcItem.right - rcItem.left;
4069     size.cy = rcItem.bottom - rcItem.top;
4070
4071     hdcOrig = GetDC(infoPtr->hwndSelf);
4072     hdc = CreateCompatibleDC(hdcOrig);
4073     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4074     hOldbmp = SelectObject(hdc, hbmp);
4075
4076     rcItem.left = rcItem.top = 0;
4077     rcItem.right = size.cx;
4078     rcItem.bottom = size.cy;
4079     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4080     
4081     pos.x = pos.y = 0;
4082     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4083     {
4084         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4085         SelectObject(hdc, hOldbmp);
4086         ImageList_Add(dragList, hbmp, 0);
4087     }
4088     else
4089         SelectObject(hdc, hOldbmp);
4090
4091     DeleteObject(hbmp);
4092     DeleteDC(hdc);
4093     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4094
4095     TRACE("ret=%p\n", dragList);
4096
4097     return dragList;
4098 }
4099
4100
4101 /***
4102  * DESCRIPTION:
4103  * Removes all listview items and subitems.
4104  *
4105  * PARAMETER(S):
4106  * [I] infoPtr : valid pointer to the listview structure
4107  *
4108  * RETURN:
4109  *   SUCCESS : TRUE
4110  *   FAILURE : FALSE
4111  */
4112 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4113 {
4114     NMLISTVIEW nmlv;
4115     HDPA hdpaSubItems = NULL;
4116     BOOL bSuppress;
4117     ITEMHDR *hdrItem;
4118     INT i, j;
4119
4120     TRACE("()\n");
4121
4122     /* we do it directly, to avoid notifications */
4123     ranges_clear(infoPtr->selectionRanges);
4124     infoPtr->nSelectionMark = -1;
4125     infoPtr->nFocusedItem = -1;
4126     SetRectEmpty(&infoPtr->rcFocus);
4127     /* But we are supposed to leave nHotItem as is! */
4128
4129
4130     /* send LVN_DELETEALLITEMS notification */
4131     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4132     nmlv.iItem = -1;
4133     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4134
4135     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4136     {
4137         /* send LVN_DELETEITEM notification, if not supressed */
4138         if (!bSuppress) notify_deleteitem(infoPtr, i);
4139         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4140         {
4141             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4142             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4143             {
4144                 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4145                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4146                 Free(hdrItem);
4147             }
4148             DPA_Destroy(hdpaSubItems);
4149             DPA_DeletePtr(infoPtr->hdpaItems, i);
4150         }
4151         DPA_DeletePtr(infoPtr->hdpaPosX, i);
4152         DPA_DeletePtr(infoPtr->hdpaPosY, i);
4153         infoPtr->nItemCount --;
4154     }
4155     
4156     LISTVIEW_UpdateScroll(infoPtr);
4157
4158     LISTVIEW_InvalidateList(infoPtr);
4159     
4160     return TRUE;
4161 }
4162
4163 /***
4164  * DESCRIPTION:
4165  * Scrolls, and updates the columns, when a column is changing width.
4166  *
4167  * PARAMETER(S):
4168  * [I] infoPtr : valid pointer to the listview structure
4169  * [I] nColumn : column to scroll
4170  * [I] dx : amount of scroll, in pixels
4171  *
4172  * RETURN:
4173  *   None.
4174  */
4175 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4176 {
4177     COLUMN_INFO *lpColumnInfo;
4178     RECT rcOld, rcCol;
4179     INT nCol;
4180    
4181     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4182     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4183     rcCol = lpColumnInfo->rcHeader;
4184     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4185         rcCol.left = rcCol.right;
4186     
4187     /* ajust the other columns */
4188     for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4189     {
4190         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4191         lpColumnInfo->rcHeader.left += dx;
4192         lpColumnInfo->rcHeader.right += dx;
4193     }
4194
4195     /* do not update screen if not in report mode */
4196     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4197     
4198     /* if we have a focus, must first erase the focus rect */
4199     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4200     
4201     /* Need to reset the item width when inserting a new column */
4202     infoPtr->nItemWidth += dx;
4203
4204     LISTVIEW_UpdateScroll(infoPtr);
4205
4206     /* scroll to cover the deleted column, and invalidate for redraw */
4207     rcOld = infoPtr->rcList;
4208     rcOld.left = rcCol.left;
4209     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4210     
4211     /* we can restore focus now */
4212     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4213 }
4214
4215 /***
4216  * DESCRIPTION:
4217  * Removes a column from the listview control.
4218  *
4219  * PARAMETER(S):
4220  * [I] infoPtr : valid pointer to the listview structure
4221  * [I] nColumn : column index
4222  *
4223  * RETURN:
4224  *   SUCCESS : TRUE
4225  *   FAILURE : FALSE
4226  */
4227 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4228 {
4229     RECT rcCol;
4230     
4231     TRACE("nColumn=%d\n", nColumn);
4232
4233     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4234            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4235
4236     /* While the MSDN specifically says that column zero should not be deleted,
4237        what actually happens is that the column itself is deleted but no items or subitems
4238        are removed.
4239      */
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) && nColumn)
4250     {
4251         SUBITEM_INFO *lpSubItem, *lpDelItem;
4252         HDPA hdpaSubItems;
4253         INT nItem, nSubItem, i;
4254         
4255         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4256         {
4257             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4258             nSubItem = 0;
4259             lpDelItem = 0;
4260             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4261             {
4262                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4263                 if (lpSubItem->iSubItem == nColumn)
4264                 {
4265                     nSubItem = i;
4266                     lpDelItem = lpSubItem;
4267                 }
4268                 else if (lpSubItem->iSubItem > nColumn) 
4269                 {
4270                     lpSubItem->iSubItem--;
4271                 }
4272             }
4273
4274             /* if we found our subitem, zapp it */      
4275             if (nSubItem > 0)
4276             {
4277                 /* free string */
4278                 if (is_textW(lpDelItem->hdr.pszText))
4279                     Free(lpDelItem->hdr.pszText);
4280
4281                 /* free item */
4282                 Free(lpDelItem);
4283
4284                 /* free dpa memory */
4285                 DPA_DeletePtr(hdpaSubItems, nSubItem);
4286             }
4287         }
4288     }
4289
4290     /* update the other column info */
4291     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4292         LISTVIEW_InvalidateList(infoPtr);
4293     else
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 LVM_GETITEMTEXT), 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 }