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