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