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