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