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