dbghelp: Brought dbghelp.h a bit closer to PSDK definitions.
[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   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3177   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3178   {
3179     if (infoPtr->dwStyle & LVS_SINGLESEL)
3180     {
3181       bResult = TRUE;
3182       LISTVIEW_SetSelection(infoPtr, nItem);
3183     }
3184     else
3185     {
3186       if (wShift)
3187       {
3188         bResult = TRUE;
3189         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3190       }
3191       else if (wCtrl)
3192       {
3193         LVITEMW lvItem;
3194         lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3195         lvItem.stateMask = LVIS_SELECTED;
3196         LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3197
3198         if (lvItem.state & LVIS_SELECTED)
3199             infoPtr->nSelectionMark = nItem;
3200
3201         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3202       }
3203       else
3204       {
3205         bResult = TRUE;
3206         LISTVIEW_SetSelection(infoPtr, nItem);
3207       }
3208     }
3209     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3210   }
3211
3212   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3213   return bResult;
3214 }
3215
3216 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3217 {
3218     LVHITTESTINFO lvHitTestInfo;
3219
3220     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3221     lvHitTestInfo.pt.x = pt.x;
3222     lvHitTestInfo.pt.y = pt.y;
3223
3224     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3225
3226     lpLVItem->mask = LVIF_PARAM;
3227     lpLVItem->iItem = lvHitTestInfo.iItem;
3228     lpLVItem->iSubItem = 0;
3229
3230     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3231 }
3232
3233 /***
3234  * DESCRIPTION:
3235  * Called when the mouse is being actively tracked and has hovered for a specified
3236  * amount of time
3237  *
3238  * PARAMETER(S):
3239  * [I] infoPtr : valid pointer to the listview structure
3240  * [I] fwKeys : key indicator
3241  * [I] x,y : mouse position
3242  *
3243  * RETURN:
3244  *   0 if the message was processed, non-zero if there was an error
3245  *
3246  * INFO:
3247  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3248  * over the item for a certain period of time.
3249  *
3250  */
3251 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3252 {
3253     if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3254     {
3255         LVITEMW item;
3256         POINT pt;
3257
3258         pt.x = x;
3259         pt.y = y;
3260
3261         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3262             LISTVIEW_SetSelection(infoPtr, item.iItem);
3263     }
3264
3265     return 0;
3266 }
3267
3268 /***
3269  * DESCRIPTION:
3270  * Called whenever WM_MOUSEMOVE is received.
3271  *
3272  * PARAMETER(S):
3273  * [I] infoPtr : valid pointer to the listview structure
3274  * [I] fwKeys : key indicator
3275  * [I] x,y : mouse position
3276  *
3277  * RETURN:
3278  *   0 if the message is processed, non-zero if there was an error
3279  */
3280 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3281 {
3282     TRACKMOUSEEVENT trackinfo;
3283
3284     if (!(fwKeys & MK_LBUTTON))
3285         infoPtr->bLButtonDown = FALSE;
3286
3287     if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3288     {
3289         LVHITTESTINFO lvHitTestInfo;
3290         NMLISTVIEW nmlv;
3291
3292         lvHitTestInfo.pt = infoPtr->ptClickPos;
3293         LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3294
3295         ZeroMemory(&nmlv, sizeof(nmlv));
3296         nmlv.iItem = lvHitTestInfo.iItem;
3297         nmlv.ptAction = infoPtr->ptClickPos;
3298
3299         notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3300
3301         return 0;
3302     }
3303     else
3304         infoPtr->bLButtonDown = FALSE;
3305
3306   /* see if we are supposed to be tracking mouse hovering */
3307   if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3308      /* fill in the trackinfo struct */
3309      trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3310      trackinfo.dwFlags = TME_QUERY;
3311      trackinfo.hwndTrack = infoPtr->hwndSelf;
3312      trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3313
3314      /* see if we are already tracking this hwnd */
3315      _TrackMouseEvent(&trackinfo);
3316
3317      if(!(trackinfo.dwFlags & TME_HOVER)) {
3318        trackinfo.dwFlags = TME_HOVER;
3319
3320        /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3321        _TrackMouseEvent(&trackinfo);
3322     }
3323   }
3324
3325   return 0;
3326 }
3327
3328
3329 /***
3330  * Tests wheather the item is assignable to a list with style lStyle 
3331  */
3332 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3333 {
3334     if ( (lpLVItem->mask & LVIF_TEXT) && 
3335         (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3336         (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3337     
3338     return TRUE;
3339 }
3340
3341
3342 /***
3343  * DESCRIPTION:
3344  * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3345  *
3346  * PARAMETER(S):
3347  * [I] infoPtr : valid pointer to the listview structure
3348  * [I] lpLVItem : valid pointer to new item atttributes
3349  * [I] isNew : the item being set is being inserted
3350  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3351  * [O] bChanged : will be set to TRUE if the item really changed
3352  *
3353  * RETURN:
3354  *   SUCCESS : TRUE
3355  *   FAILURE : FALSE
3356  */
3357 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3358 {
3359     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3360     ITEM_INFO *lpItem;
3361     NMLISTVIEW nmlv;
3362     UINT uChanged = 0;
3363     LVITEMW item;
3364
3365     TRACE("()\n");
3366
3367     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3368     
3369     if (lpLVItem->mask == 0) return TRUE;   
3370
3371     if (infoPtr->dwStyle & LVS_OWNERDATA)
3372     {
3373         /* a virtual listview we stores only selection and focus */
3374         if (lpLVItem->mask & ~LVIF_STATE)
3375             return FALSE;
3376         lpItem = NULL;
3377     }
3378     else
3379     {
3380         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3381         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3382         assert (lpItem);
3383     }
3384
3385     /* we need to get the lParam and state of the item */
3386     item.iItem = lpLVItem->iItem;
3387     item.iSubItem = lpLVItem->iSubItem;
3388     item.mask = LVIF_STATE | LVIF_PARAM;
3389     item.stateMask = ~0;
3390     item.state = 0;
3391     item.lParam = 0;
3392     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3393
3394     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3395     /* determine what fields will change */    
3396     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3397         uChanged |= LVIF_STATE;
3398
3399     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3400         uChanged |= LVIF_IMAGE;
3401
3402     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3403         uChanged |= LVIF_PARAM;
3404
3405     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3406         uChanged |= LVIF_INDENT;
3407
3408     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3409         uChanged |= LVIF_TEXT;
3410    
3411     TRACE("uChanged=0x%x\n", uChanged); 
3412     if (!uChanged) return TRUE;
3413     *bChanged = TRUE;
3414     
3415     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3416     nmlv.iItem = lpLVItem->iItem;
3417     nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3418     nmlv.uOldState = item.state;
3419     nmlv.uChanged = uChanged;
3420     nmlv.lParam = item.lParam;
3421     
3422     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3423     /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3424     /* are enabled */
3425     if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3426     {
3427       HWND hwndSelf = infoPtr->hwndSelf;
3428
3429       if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3430         return FALSE;
3431       if (!IsWindow(hwndSelf))
3432         return FALSE;
3433     }
3434
3435     /* copy information */
3436     if (lpLVItem->mask & LVIF_TEXT)
3437         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3438
3439     if (lpLVItem->mask & LVIF_IMAGE)
3440         lpItem->hdr.iImage = lpLVItem->iImage;
3441
3442     if (lpLVItem->mask & LVIF_PARAM)
3443         lpItem->lParam = lpLVItem->lParam;
3444
3445     if (lpLVItem->mask & LVIF_INDENT)
3446         lpItem->iIndent = lpLVItem->iIndent;
3447
3448     if (uChanged & LVIF_STATE)
3449     {
3450         if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3451         {
3452             lpItem->state &= ~lpLVItem->stateMask;
3453             lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3454         }
3455         if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3456         {
3457             if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3458             ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3459         }
3460         else if (lpLVItem->stateMask & LVIS_SELECTED)
3461             ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3462         
3463         /* if we are asked to change focus, and we manage it, do it */
3464         if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3465         {
3466             if (lpLVItem->state & LVIS_FOCUSED)
3467             {
3468                 LISTVIEW_SetItemFocus(infoPtr, -1);
3469                 infoPtr->nFocusedItem = lpLVItem->iItem;
3470                 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3471             }
3472             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3473                 infoPtr->nFocusedItem = -1;
3474         }
3475     }
3476
3477     /* if we're inserting the item, we're done */
3478     if (isNew) return TRUE;
3479     
3480     /* send LVN_ITEMCHANGED notification */
3481     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3482     if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3483
3484     return TRUE;
3485 }
3486
3487 /***
3488  * DESCRIPTION:
3489  * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3490  *
3491  * PARAMETER(S):
3492  * [I] infoPtr : valid pointer to the listview structure
3493  * [I] lpLVItem : valid pointer to new subitem atttributes
3494  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3495  * [O] bChanged : will be set to TRUE if the item really changed
3496  *
3497  * RETURN:
3498  *   SUCCESS : TRUE
3499  *   FAILURE : FALSE
3500  */
3501 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3502 {
3503     HDPA hdpaSubItems;
3504     SUBITEM_INFO *lpSubItem;
3505
3506     /* we do not support subitems for virtual listviews */
3507     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3508     
3509     /* set subitem only if column is present */
3510     if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3511    
3512     /* First do some sanity checks */
3513     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3514     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3515    
3516     /* get the subitem structure, and create it if not there */
3517     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3518     assert (hdpaSubItems);
3519     
3520     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3521     if (!lpSubItem)
3522     {
3523         SUBITEM_INFO *tmpSubItem;
3524         INT i;
3525
3526         lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3527         if (!lpSubItem) return FALSE;
3528         /* we could binary search here, if need be...*/
3529         for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3530         {
3531             tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3532             if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3533         }
3534         if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3535         {
3536             Free(lpSubItem);
3537             return FALSE;
3538         }
3539         lpSubItem->iSubItem = lpLVItem->iSubItem;
3540         lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3541         *bChanged = TRUE;
3542     }
3543     
3544     if (lpLVItem->mask & LVIF_IMAGE)
3545         if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3546         {
3547             lpSubItem->hdr.iImage = lpLVItem->iImage;
3548             *bChanged = TRUE;
3549         }
3550
3551     if (lpLVItem->mask & LVIF_TEXT)
3552         if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3553         {
3554             textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3555             *bChanged = TRUE;
3556         }
3557
3558     return TRUE;
3559 }
3560
3561 /***
3562  * DESCRIPTION:
3563  * Sets item attributes.
3564  *
3565  * PARAMETER(S):
3566  * [I] infoPtr : valid pointer to the listview structure
3567  * [I] lpLVItem : new item atttributes
3568  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3569  *
3570  * RETURN:
3571  *   SUCCESS : TRUE
3572  *   FAILURE : FALSE
3573  */
3574 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3575 {
3576     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3577     HWND hwndSelf = infoPtr->hwndSelf;
3578     LPWSTR pszText = NULL;
3579     BOOL bResult, bChanged = FALSE;
3580     
3581     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3582
3583     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3584         return FALSE;
3585
3586     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3587     if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3588     {
3589         pszText = lpLVItem->pszText;
3590         ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3591     }
3592     
3593     /* actually set the fields */
3594     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3595     
3596     if (lpLVItem->iSubItem)
3597         bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3598     else
3599         bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3600     if (!IsWindow(hwndSelf))
3601         return FALSE;
3602
3603     /* redraw item, if necessary */
3604     if (bChanged && !infoPtr->bIsDrawing)
3605     {
3606         /* this little optimization eliminates some nasty flicker */
3607         if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3608              (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3609             LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3610         else
3611             LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3612     }
3613     /* restore text */
3614     if (pszText)
3615     {
3616         textfreeT(lpLVItem->pszText, isW);
3617         ((LVITEMW *)lpLVItem)->pszText = pszText;
3618     }
3619
3620     return bResult;
3621 }
3622
3623 /***
3624  * DESCRIPTION:
3625  * Retrieves the index of the item at coordinate (0, 0) of the client area.
3626  *
3627  * PARAMETER(S):
3628  * [I] infoPtr : valid pointer to the listview structure
3629  *
3630  * RETURN:
3631  * item index
3632  */
3633 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3634 {
3635     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3636     INT nItem = 0;
3637     SCROLLINFO scrollInfo;
3638
3639     scrollInfo.cbSize = sizeof(SCROLLINFO);
3640     scrollInfo.fMask = SIF_POS;
3641
3642     if (uView == LVS_LIST)
3643     {
3644         if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3645             nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3646     }
3647     else if (uView == LVS_REPORT)
3648     {
3649         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3650             nItem = scrollInfo.nPos;
3651     } 
3652     else
3653     {
3654         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3655             nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3656     }
3657
3658     TRACE("nItem=%d\n", nItem);
3659     
3660     return nItem;
3661 }
3662
3663
3664 /***
3665  * DESCRIPTION:
3666  * Erases the background of the given rectangle
3667  *
3668  * PARAMETER(S):
3669  * [I] infoPtr : valid pointer to the listview structure
3670  * [I] hdc : device context handle
3671  * [I] lprcBox : clipping rectangle
3672  *
3673  * RETURN:
3674  *   Success: TRUE
3675  *   Failure: FALSE
3676  */
3677 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3678 {
3679     if (!infoPtr->hBkBrush) return FALSE;
3680
3681     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3682
3683     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3684 }
3685
3686 /***
3687  * DESCRIPTION:
3688  * Draws an item.
3689  *
3690  * PARAMETER(S):
3691  * [I] infoPtr : valid pointer to the listview structure
3692  * [I] hdc : device context handle
3693  * [I] nItem : item index
3694  * [I] nSubItem : subitem index
3695  * [I] pos : item position in client coordinates
3696  * [I] cdmode : custom draw mode
3697  *
3698  * RETURN:
3699  *   Success: TRUE
3700  *   Failure: FALSE
3701  */
3702 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3703 {
3704     UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3705     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3706     static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3707     DWORD cdsubitemmode = CDRF_DODEFAULT;
3708     RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3709     NMLVCUSTOMDRAW nmlvcd;
3710     HIMAGELIST himl;
3711     LVITEMW lvItem;
3712
3713     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3714
3715     /* get information needed for drawing the item */
3716     lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3717     if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3718     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3719     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3720     lvItem.iItem = nItem;
3721     lvItem.iSubItem = nSubItem;
3722     lvItem.state = 0;
3723     lvItem.lParam = 0;
3724     lvItem.cchTextMax = DISP_TEXT_SIZE;
3725     lvItem.pszText = szDispText;
3726     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3727     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
3728         lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3729     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3730     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3731
3732     /* now check if we need to update the focus rectangle */
3733     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3734     
3735     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3736     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3737     OffsetRect(&rcBox, pos.x, pos.y);
3738     OffsetRect(&rcState, pos.x, pos.y);
3739     OffsetRect(&rcIcon, pos.x, pos.y);
3740     OffsetRect(&rcLabel, pos.x, pos.y);
3741     TRACE("    rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n", 
3742         debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3743
3744     /* fill in the custom draw structure */
3745     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3746
3747     if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3748     if (cdmode & CDRF_NOTIFYITEMDRAW)
3749         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3750     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3751     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3752     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3753     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3754     {
3755         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3756         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3757     }
3758     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3759         prepaint_setup(infoPtr, hdc, &nmlvcd);
3760
3761     /* in full row select, subitems, will just use main item's colors */
3762     if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3763         nmlvcd.clrTextBk = CLR_NONE;
3764     
3765     /* state icons */
3766     if (infoPtr->himlState && !IsRectEmpty(&rcState))
3767     {
3768         UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3769         if (uStateImage)
3770         {
3771              TRACE("uStateImage=%d\n", uStateImage);
3772              ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3773         }
3774     }
3775
3776     /* small icons */
3777     himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3778     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3779     {
3780         TRACE("iImage=%d\n", lvItem.iImage);
3781         ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3782                         (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3783     }
3784
3785     /* Don't bother painting item being edited */
3786     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3787
3788     /* draw the selection background, if we're drawing the main item */
3789     if (nSubItem == 0)
3790     {
3791         rcSelect = rcLabel;
3792         if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3793             rcSelect.right = rcBox.right;
3794    
3795         if (nmlvcd.clrTextBk != CLR_NONE) 
3796             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3797         if(lprcFocus) *lprcFocus = rcSelect;
3798     }
3799    
3800     /* figure out the text drawing flags */
3801     uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3802     if (uView == LVS_ICON)
3803         uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3804     else if (nSubItem)
3805     {
3806         switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3807         {
3808         case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
3809         case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3810         default:            uFormat |= DT_LEFT;
3811         }
3812     }
3813     if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3814     {
3815         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3816         else rcLabel.left += LABEL_HOR_PADDING;
3817     }
3818     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3819     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3820
3821 postpaint:
3822     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3823         notify_postpaint(infoPtr, &nmlvcd);
3824     return TRUE;
3825 }
3826
3827 /***
3828  * DESCRIPTION:
3829  * Draws listview items when in owner draw mode.
3830  *
3831  * PARAMETER(S):
3832  * [I] infoPtr : valid pointer to the listview structure
3833  * [I] hdc : device context handle
3834  *
3835  * RETURN:
3836  * None
3837  */
3838 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3839 {
3840     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3841     DWORD cditemmode = CDRF_DODEFAULT;
3842     NMLVCUSTOMDRAW nmlvcd;
3843     POINT Origin, Position;
3844     DRAWITEMSTRUCT dis;
3845     LVITEMW item;
3846     
3847     TRACE("()\n");
3848
3849     ZeroMemory(&dis, sizeof(dis));
3850     
3851     /* Get scroll info once before loop */
3852     LISTVIEW_GetOrigin(infoPtr, &Origin);
3853     
3854     /* iterate through the invalidated rows */
3855     while(iterator_next(i))
3856     {
3857         item.iItem = i->nItem;
3858         item.iSubItem = 0;
3859         item.mask = LVIF_PARAM | LVIF_STATE;
3860         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3861         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3862            
3863         dis.CtlType = ODT_LISTVIEW;
3864         dis.CtlID = uID;
3865         dis.itemID = item.iItem;
3866         dis.itemAction = ODA_DRAWENTIRE;
3867         dis.itemState = 0;
3868         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3869         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3870         dis.hwndItem = infoPtr->hwndSelf;
3871         dis.hDC = hdc;
3872         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3873         dis.rcItem.left = Position.x + Origin.x;
3874         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3875         dis.rcItem.top = Position.y + Origin.y;
3876         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3877         dis.itemData = item.lParam;
3878
3879         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3880
3881     /*
3882      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3883      * structure for the rest. of the paint cycle
3884      */
3885         customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3886         if (cdmode & CDRF_NOTIFYITEMDRAW)
3887             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3888     
3889         if (!(cditemmode & CDRF_SKIPDEFAULT))
3890         {
3891             prepaint_setup (infoPtr, hdc, &nmlvcd);
3892             SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3893         }
3894
3895         if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3896             notify_postpaint(infoPtr, &nmlvcd);
3897     }
3898 }
3899
3900 /***
3901  * DESCRIPTION:
3902  * Draws listview items when in report display mode.
3903  *
3904  * PARAMETER(S):
3905  * [I] infoPtr : valid pointer to the listview structure
3906  * [I] hdc : device context handle
3907  * [I] cdmode : custom draw mode
3908  *
3909  * RETURN:
3910  * None
3911  */
3912 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3913 {
3914     INT rgntype;
3915     RECT rcClip, rcItem;
3916     POINT Origin, Position;
3917     RANGE colRange;
3918     ITERATOR j;
3919
3920     TRACE("()\n");
3921
3922     /* figure out what to draw */
3923     rgntype = GetClipBox(hdc, &rcClip);
3924     if (rgntype == NULLREGION) return;
3925     
3926     /* Get scroll info once before loop */
3927     LISTVIEW_GetOrigin(infoPtr, &Origin);
3928     
3929     /* narrow down the columns we need to paint */
3930     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3931     {
3932         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3933         if (rcItem.right + Origin.x >= rcClip.left) break;
3934     }
3935     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3936     {
3937         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3938         if (rcItem.left + Origin.x < rcClip.right) break;
3939     }
3940     iterator_rangeitems(&j, colRange);
3941
3942     /* in full row select, we _have_ to draw the main item */
3943     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3944         j.nSpecial = 0;
3945
3946     /* iterate through the invalidated rows */
3947     while(iterator_next(i))
3948     {
3949         /* iterate through the invalidated columns */
3950         while(iterator_next(&j))
3951         {
3952             LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3953             Position.x += Origin.x;
3954             Position.y += Origin.y;
3955
3956             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3957             {
3958                 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3959                 rcItem.top = 0;
3960                 rcItem.bottom = infoPtr->nItemHeight;
3961                 OffsetRect(&rcItem, Position.x, Position.y);
3962                 if (!RectVisible(hdc, &rcItem)) continue;
3963             }
3964
3965             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3966         }
3967     }
3968     iterator_destroy(&j);
3969 }
3970
3971 /***
3972  * DESCRIPTION:
3973  * Draws listview items when in list display mode.
3974  *
3975  * PARAMETER(S):
3976  * [I] infoPtr : valid pointer to the listview structure
3977  * [I] hdc : device context handle
3978  * [I] cdmode : custom draw mode
3979  *
3980  * RETURN:
3981  * None
3982  */
3983 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3984 {
3985     POINT Origin, Position;
3986
3987     /* Get scroll info once before loop */
3988     LISTVIEW_GetOrigin(infoPtr, &Origin);
3989     
3990     while(iterator_prev(i))
3991     {
3992         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3993         Position.x += Origin.x;
3994         Position.y += Origin.y;
3995
3996         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3997     }
3998 }
3999
4000
4001 /***
4002  * DESCRIPTION:
4003  * Draws listview items.
4004  *
4005  * PARAMETER(S):
4006  * [I] infoPtr : valid pointer to the listview structure
4007  * [I] hdc : device context handle
4008  *
4009  * RETURN:
4010  * NoneX
4011  */
4012 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
4013 {
4014     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4015     COLORREF oldTextColor, oldClrTextBk, oldClrText;
4016     NMLVCUSTOMDRAW nmlvcd;
4017     HFONT hOldFont;
4018     DWORD cdmode;
4019     INT oldBkMode;
4020     RECT rcClient;
4021     ITERATOR i;
4022
4023     LISTVIEW_DUMP(infoPtr);
4024   
4025     infoPtr->bIsDrawing = TRUE;
4026
4027     /* save dc values we're gonna trash while drawing */
4028     hOldFont = SelectObject(hdc, infoPtr->hFont);
4029     oldBkMode = GetBkMode(hdc);
4030     infoPtr->clrTextBkDefault = GetBkColor(hdc);
4031     oldTextColor = GetTextColor(hdc);
4032
4033     oldClrTextBk = infoPtr->clrTextBk;
4034     oldClrText   = infoPtr->clrText;
4035    
4036     infoPtr->cditemmode = CDRF_DODEFAULT;
4037
4038     GetClientRect(infoPtr->hwndSelf, &rcClient);
4039     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4040     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4041     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4042     prepaint_setup(infoPtr, hdc, &nmlvcd);
4043
4044     /* Use these colors to draw the items */
4045     infoPtr->clrTextBk = nmlvcd.clrTextBk;
4046     infoPtr->clrText = nmlvcd.clrText;
4047
4048     /* nothing to draw */
4049     if(infoPtr->nItemCount == 0) goto enddraw;
4050
4051     /* figure out what we need to draw */
4052     iterator_visibleitems(&i, infoPtr, hdc);
4053     
4054     /* send cache hint notification */
4055     if (infoPtr->dwStyle & LVS_OWNERDATA)
4056     {
4057         RANGE range = iterator_range(&i);
4058         NMLVCACHEHINT nmlv;
4059         
4060         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4061         nmlv.iFrom = range.lower;
4062         nmlv.iTo   = range.upper - 1;
4063         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4064     }
4065
4066     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4067         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4068     else
4069     {
4070         if (uView == LVS_REPORT)
4071             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4072         else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4073             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4074
4075         /* if we have a focus rect, draw it */
4076         if (infoPtr->bFocus)
4077             DrawFocusRect(hdc, &infoPtr->rcFocus);
4078     }
4079     iterator_destroy(&i);
4080     
4081 enddraw:
4082     if (cdmode & CDRF_NOTIFYPOSTPAINT)
4083         notify_postpaint(infoPtr, &nmlvcd);
4084
4085     infoPtr->clrTextBk = oldClrTextBk;
4086     infoPtr->clrText = oldClrText;
4087
4088     SelectObject(hdc, hOldFont);
4089     SetBkMode(hdc, oldBkMode);
4090     SetBkColor(hdc, infoPtr->clrTextBkDefault);
4091     SetTextColor(hdc, oldTextColor);
4092     infoPtr->bIsDrawing = FALSE;
4093 }
4094
4095
4096 /***
4097  * DESCRIPTION:
4098  * Calculates the approximate width and height of a given number of items.
4099  *
4100  * PARAMETER(S):
4101  * [I] infoPtr : valid pointer to the listview structure
4102  * [I] nItemCount : number of items
4103  * [I] wWidth : width
4104  * [I] wHeight : height
4105  *
4106  * RETURN:
4107  * Returns a DWORD. The width in the low word and the height in high word.
4108  */
4109 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4110                                             WORD wWidth, WORD wHeight)
4111 {
4112   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4113   INT nItemCountPerColumn = 1;
4114   INT nColumnCount = 0;
4115   DWORD dwViewRect = 0;
4116
4117   if (nItemCount == -1)
4118     nItemCount = infoPtr->nItemCount;
4119
4120   if (uView == LVS_LIST)
4121   {
4122     if (wHeight == 0xFFFF)
4123     {
4124       /* use current height */
4125       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4126     }
4127
4128     if (wHeight < infoPtr->nItemHeight)
4129       wHeight = infoPtr->nItemHeight;
4130
4131     if (nItemCount > 0)
4132     {
4133       if (infoPtr->nItemHeight > 0)
4134       {
4135         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4136         if (nItemCountPerColumn == 0)
4137           nItemCountPerColumn = 1;
4138
4139         if (nItemCount % nItemCountPerColumn != 0)
4140           nColumnCount = nItemCount / nItemCountPerColumn;
4141         else
4142           nColumnCount = nItemCount / nItemCountPerColumn + 1;
4143       }
4144     }
4145
4146     /* Microsoft padding magic */
4147     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4148     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4149
4150     dwViewRect = MAKELONG(wWidth, wHeight);
4151   }
4152   else if (uView == LVS_REPORT)
4153   {
4154     RECT rcBox;
4155
4156     if (infoPtr->nItemCount > 0)
4157     {
4158       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4159       wWidth = rcBox.right - rcBox.left;
4160       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4161     }
4162     else
4163     {
4164       /* use current height and width */
4165       if (wHeight == 0xffff)
4166           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4167       if (wWidth == 0xffff)
4168           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4169     }
4170
4171     dwViewRect = MAKELONG(wWidth, wHeight);
4172   }
4173   else if (uView == LVS_SMALLICON)
4174     FIXME("uView == LVS_SMALLICON: not implemented\n");
4175   else if (uView == LVS_ICON)
4176     FIXME("uView == LVS_ICON: not implemented\n");
4177
4178   return dwViewRect;
4179 }
4180
4181
4182 /***
4183  * DESCRIPTION:
4184  * Create a drag image list for the specified item.
4185  *
4186  * PARAMETER(S):
4187  * [I] infoPtr : valid pointer to the listview structure
4188  * [I] iItem   : index of item
4189  * [O] lppt    : Upperr-left corner of the image
4190  *
4191  * RETURN:
4192  * Returns a handle to the image list if successful, NULL otherwise.
4193  */
4194 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4195 {
4196     RECT rcItem;
4197     SIZE size;
4198     POINT pos;
4199     HDC hdc, hdcOrig;
4200     HBITMAP hbmp, hOldbmp;
4201     HIMAGELIST dragList = 0;
4202     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4203
4204     if (iItem < 0 || iItem >= infoPtr->nItemCount)
4205         return 0;
4206
4207     rcItem.left = LVIR_BOUNDS;
4208     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4209         return 0;
4210
4211     lppt->x = rcItem.left;
4212     lppt->y = rcItem.top;
4213
4214     size.cx = rcItem.right - rcItem.left;
4215     size.cy = rcItem.bottom - rcItem.top;
4216
4217     hdcOrig = GetDC(infoPtr->hwndSelf);
4218     hdc = CreateCompatibleDC(hdcOrig);
4219     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4220     hOldbmp = SelectObject(hdc, hbmp);
4221
4222     rcItem.left = rcItem.top = 0;
4223     rcItem.right = size.cx;
4224     rcItem.bottom = size.cy;
4225     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4226     
4227     pos.x = pos.y = 0;
4228     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4229     {
4230         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4231         SelectObject(hdc, hOldbmp);
4232         ImageList_Add(dragList, hbmp, 0);
4233     }
4234     else
4235         SelectObject(hdc, hOldbmp);
4236
4237     DeleteObject(hbmp);
4238     DeleteDC(hdc);
4239     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4240
4241     TRACE("ret=%p\n", dragList);
4242
4243     return dragList;
4244 }
4245
4246
4247 /***
4248  * DESCRIPTION:
4249  * Removes all listview items and subitems.
4250  *
4251  * PARAMETER(S):
4252  * [I] infoPtr : valid pointer to the listview structure
4253  *
4254  * RETURN:
4255  *   SUCCESS : TRUE
4256  *   FAILURE : FALSE
4257  */
4258 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4259 {
4260     NMLISTVIEW nmlv;
4261     HDPA hdpaSubItems = NULL;
4262     BOOL bSuppress;
4263     ITEMHDR *hdrItem;
4264     INT i, j;
4265
4266     TRACE("()\n");
4267
4268     /* we do it directly, to avoid notifications */
4269     ranges_clear(infoPtr->selectionRanges);
4270     infoPtr->nSelectionMark = -1;
4271     infoPtr->nFocusedItem = -1;
4272     SetRectEmpty(&infoPtr->rcFocus);
4273     /* But we are supposed to leave nHotItem as is! */
4274
4275
4276     /* send LVN_DELETEALLITEMS notification */
4277     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4278     nmlv.iItem = -1;
4279     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4280
4281     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4282     {
4283         /* send LVN_DELETEITEM notification, if not suppressed */
4284         if (!bSuppress) notify_deleteitem(infoPtr, i);
4285         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4286         {
4287             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4288             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4289             {
4290                 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4291                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4292                 Free(hdrItem);
4293             }
4294             DPA_Destroy(hdpaSubItems);
4295             DPA_DeletePtr(infoPtr->hdpaItems, i);
4296         }
4297         DPA_DeletePtr(infoPtr->hdpaPosX, i);
4298         DPA_DeletePtr(infoPtr->hdpaPosY, i);
4299         infoPtr->nItemCount --;
4300     }
4301     
4302     LISTVIEW_UpdateScroll(infoPtr);
4303
4304     LISTVIEW_InvalidateList(infoPtr);
4305     
4306     return TRUE;
4307 }
4308
4309 /***
4310  * DESCRIPTION:
4311  * Scrolls, and updates the columns, when a column is changing width.
4312  *
4313  * PARAMETER(S):
4314  * [I] infoPtr : valid pointer to the listview structure
4315  * [I] nColumn : column to scroll
4316  * [I] dx : amount of scroll, in pixels
4317  *
4318  * RETURN:
4319  *   None.
4320  */
4321 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4322 {
4323     COLUMN_INFO *lpColumnInfo;
4324     RECT rcOld, rcCol;
4325     POINT ptOrigin;
4326     INT nCol;
4327    
4328     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4329     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4330     rcCol = lpColumnInfo->rcHeader;
4331     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4332         rcCol.left = rcCol.right;
4333     
4334     /* ajust the other columns */
4335     for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4336     {
4337         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4338         lpColumnInfo->rcHeader.left += dx;
4339         lpColumnInfo->rcHeader.right += dx;
4340     }
4341
4342     /* do not update screen if not in report mode */
4343     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4344     
4345     /* if we have a focus, must first erase the focus rect */
4346     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4347     
4348     /* Need to reset the item width when inserting a new column */
4349     infoPtr->nItemWidth += dx;
4350
4351     LISTVIEW_UpdateScroll(infoPtr);
4352     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4353
4354     /* scroll to cover the deleted column, and invalidate for redraw */
4355     rcOld = infoPtr->rcList;
4356     rcOld.left = ptOrigin.x + rcCol.left + dx;
4357     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4358     
4359     /* we can restore focus now */
4360     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4361 }
4362
4363 /***
4364  * DESCRIPTION:
4365  * Removes a column from the listview control.
4366  *
4367  * PARAMETER(S):
4368  * [I] infoPtr : valid pointer to the listview structure
4369  * [I] nColumn : column index
4370  *
4371  * RETURN:
4372  *   SUCCESS : TRUE
4373  *   FAILURE : FALSE
4374  */
4375 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4376 {
4377     RECT rcCol;
4378     
4379     TRACE("nColumn=%d\n", nColumn);
4380
4381     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4382            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4383
4384     /* While the MSDN specifically says that column zero should not be deleted,
4385        what actually happens is that the column itself is deleted but no items or subitems
4386        are removed.
4387      */
4388
4389     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4390     
4391     if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4392         return FALSE;
4393
4394     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4395     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4396   
4397     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4398     {
4399         SUBITEM_INFO *lpSubItem, *lpDelItem;
4400         HDPA hdpaSubItems;
4401         INT nItem, nSubItem, i;
4402         
4403         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4404         {
4405             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4406             nSubItem = 0;
4407             lpDelItem = 0;
4408             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4409             {
4410                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4411                 if (lpSubItem->iSubItem == nColumn)
4412                 {
4413                     nSubItem = i;
4414                     lpDelItem = lpSubItem;
4415                 }
4416                 else if (lpSubItem->iSubItem > nColumn) 
4417                 {
4418                     lpSubItem->iSubItem--;
4419                 }
4420             }
4421
4422             /* if we found our subitem, zapp it */      
4423             if (nSubItem > 0)
4424             {
4425                 /* free string */
4426                 if (is_textW(lpDelItem->hdr.pszText))
4427                     Free(lpDelItem->hdr.pszText);
4428
4429                 /* free item */
4430                 Free(lpDelItem);
4431
4432                 /* free dpa memory */
4433                 DPA_DeletePtr(hdpaSubItems, nSubItem);
4434             }
4435         }
4436     }
4437
4438     /* update the other column info */
4439     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4440         LISTVIEW_InvalidateList(infoPtr);
4441     else
4442         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4443
4444     return TRUE;
4445 }
4446
4447 /***
4448  * DESCRIPTION:
4449  * Invalidates the listview after an item's insertion or deletion.
4450  *
4451  * PARAMETER(S):
4452  * [I] infoPtr : valid pointer to the listview structure
4453  * [I] nItem : item index
4454  * [I] dir : -1 if deleting, 1 if inserting
4455  *
4456  * RETURN:
4457  *   None
4458  */
4459 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4460 {
4461     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4462     INT nPerCol, nItemCol, nItemRow;
4463     RECT rcScroll;
4464     POINT Origin;
4465
4466     /* if we don't refresh, what's the point of scrolling? */
4467     if (!is_redrawing(infoPtr)) return;
4468     
4469     assert (abs(dir) == 1);
4470
4471     /* arrange icons if autoarrange is on */
4472     if (is_autoarrange(infoPtr))
4473     {
4474         BOOL arrange = TRUE;
4475         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4476         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4477         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4478     }
4479
4480     /* scrollbars need updating */
4481     LISTVIEW_UpdateScroll(infoPtr);
4482
4483     /* figure out the item's position */ 
4484     if (uView == LVS_REPORT)
4485         nPerCol = infoPtr->nItemCount + 1;
4486     else if (uView == LVS_LIST)
4487         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4488     else /* LVS_ICON, or LVS_SMALLICON */
4489         return;
4490     
4491     nItemCol = nItem / nPerCol;
4492     nItemRow = nItem % nPerCol;
4493     LISTVIEW_GetOrigin(infoPtr, &Origin);
4494
4495     /* move the items below up a slot */
4496     rcScroll.left = nItemCol * infoPtr->nItemWidth;
4497     rcScroll.top = nItemRow * infoPtr->nItemHeight;
4498     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4499     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4500     OffsetRect(&rcScroll, Origin.x, Origin.y);
4501     TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4502     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4503     {
4504         TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4505         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
4506                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4507     }
4508
4509     /* report has only that column, so we're done */
4510     if (uView == LVS_REPORT) return;
4511
4512     /* now for LISTs, we have to deal with the columns to the right */
4513     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4514     rcScroll.top = 0;
4515     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4516     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4517     OffsetRect(&rcScroll, Origin.x, Origin.y);
4518     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4519         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4520                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4521 }
4522
4523 /***
4524  * DESCRIPTION:
4525  * Removes an item from the listview control.
4526  *
4527  * PARAMETER(S):
4528  * [I] infoPtr : valid pointer to the listview structure
4529  * [I] nItem : item index
4530  *
4531  * RETURN:
4532  *   SUCCESS : TRUE
4533  *   FAILURE : FALSE
4534  */
4535 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4536 {
4537     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4538     LVITEMW item;
4539
4540     TRACE("(nItem=%d)\n", nItem);
4541
4542     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4543     
4544     /* remove selection, and focus */
4545     item.state = 0;
4546     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4547     LISTVIEW_SetItemState(infoPtr, nItem, &item);
4548             
4549     /* send LVN_DELETEITEM notification. */
4550     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4551
4552     /* we need to do this here, because we'll be deleting stuff */  
4553     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4554         LISTVIEW_InvalidateItem(infoPtr, nItem);
4555     
4556     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4557     {
4558         HDPA hdpaSubItems;
4559         ITEMHDR *hdrItem;
4560         INT i;
4561
4562         hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);  
4563         for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4564         {
4565             hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4566             if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4567             Free(hdrItem);
4568         }
4569         DPA_Destroy(hdpaSubItems);
4570     }
4571
4572     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4573     {
4574         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4575         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4576     }
4577
4578     infoPtr->nItemCount--;
4579     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4580
4581     /* now is the invalidation fun */
4582     LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4583     return TRUE;
4584 }
4585
4586
4587 /***
4588  * DESCRIPTION:
4589  * Callback implementation for editlabel control
4590  *
4591  * PARAMETER(S):
4592  * [I] infoPtr : valid pointer to the listview structure
4593  * [I] pszText : modified text
4594  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4595  *
4596  * RETURN:
4597  *   SUCCESS : TRUE
4598  *   FAILURE : FALSE
4599  */
4600 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4601 {
4602     HWND hwndSelf = infoPtr->hwndSelf;
4603     NMLVDISPINFOW dispInfo;
4604
4605     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4606
4607     ZeroMemory(&dispInfo, sizeof(dispInfo));
4608     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4609     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4610     dispInfo.item.iSubItem = 0;
4611     dispInfo.item.stateMask = ~0;
4612     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4613     /* add the text from the edit in */
4614     dispInfo.item.mask |= LVIF_TEXT;
4615     dispInfo.item.pszText = pszText;
4616     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4617
4618     /* Do we need to update the Item Text */
4619     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4620     if (!IsWindow(hwndSelf))
4621         return FALSE;
4622     if (!pszText) return TRUE;
4623
4624     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4625     {
4626         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4627         ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4628         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4629         {
4630             LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4631             return TRUE;
4632         }
4633     }
4634
4635     ZeroMemory(&dispInfo, sizeof(dispInfo));
4636     dispInfo.item.mask = LVIF_TEXT;
4637     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4638     dispInfo.item.iSubItem = 0;
4639     dispInfo.item.pszText = pszText;
4640     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4641     return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4642 }
4643
4644 /***
4645  * DESCRIPTION:
4646  * Begin in place editing of specified list view item
4647  *
4648  * PARAMETER(S):
4649  * [I] infoPtr : valid pointer to the listview structure
4650  * [I] nItem : item index
4651  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4652  *
4653  * RETURN:
4654  *   SUCCESS : TRUE
4655  *   FAILURE : FALSE
4656  */
4657 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4658 {
4659     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4660     NMLVDISPINFOW dispInfo;
4661     RECT rect;
4662     HWND hwndSelf = infoPtr->hwndSelf;
4663
4664     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4665
4666     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4667     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4668
4669     infoPtr->nEditLabelItem = nItem;
4670
4671     /* Is the EditBox still there, if so remove it */
4672     if(infoPtr->hwndEdit != 0)
4673     {
4674         SetFocus(infoPtr->hwndSelf);
4675         infoPtr->hwndEdit = 0;
4676     }
4677
4678     LISTVIEW_SetSelection(infoPtr, nItem);
4679     LISTVIEW_SetItemFocus(infoPtr, nItem);
4680     LISTVIEW_InvalidateItem(infoPtr, nItem);
4681
4682     rect.left = LVIR_LABEL;
4683     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4684     
4685     ZeroMemory(&dispInfo, sizeof(dispInfo));
4686     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4687     dispInfo.item.iItem = nItem;
4688     dispInfo.item.iSubItem = 0;
4689     dispInfo.item.stateMask = ~0;
4690     dispInfo.item.pszText = szDispText;
4691     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4692     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4693
4694     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4695                     rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4696     if (!infoPtr->hwndEdit) return 0;
4697     
4698     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4699     {
4700         if (!IsWindow(hwndSelf))
4701             return 0;
4702         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4703         infoPtr->hwndEdit = 0;
4704         return 0;
4705     }
4706
4707     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4708     SetFocus(infoPtr->hwndEdit);
4709     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4710     return infoPtr->hwndEdit;
4711 }
4712
4713
4714 /***
4715  * DESCRIPTION:
4716  * Ensures the specified item is visible, scrolling into view if necessary.
4717  *
4718  * PARAMETER(S):
4719  * [I] infoPtr : valid pointer to the listview structure
4720  * [I] nItem : item index
4721  * [I] bPartial : partially or entirely visible
4722  *
4723  * RETURN:
4724  *   SUCCESS : TRUE
4725  *   FAILURE : FALSE
4726  */
4727 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4728 {
4729     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4730     INT nScrollPosHeight = 0;
4731     INT nScrollPosWidth = 0;
4732     INT nHorzAdjust = 0;
4733     INT nVertAdjust = 0;
4734     INT nHorzDiff = 0;
4735     INT nVertDiff = 0;
4736     RECT rcItem, rcTemp;
4737
4738     rcItem.left = LVIR_BOUNDS;
4739     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4740
4741     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4742     
4743     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4744     {
4745         /* scroll left/right, but in LVS_REPORT mode */
4746         if (uView == LVS_LIST)
4747             nScrollPosWidth = infoPtr->nItemWidth;
4748         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4749             nScrollPosWidth = 1;
4750
4751         if (rcItem.left < infoPtr->rcList.left)
4752         {
4753             nHorzAdjust = -1;
4754             if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4755         }
4756         else
4757         {
4758             nHorzAdjust = 1;
4759             if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4760         }
4761     }
4762
4763     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4764     {
4765         /* scroll up/down, but not in LVS_LIST mode */
4766         if (uView == LVS_REPORT)
4767             nScrollPosHeight = infoPtr->nItemHeight;
4768         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4769             nScrollPosHeight = 1;
4770
4771         if (rcItem.top < infoPtr->rcList.top)
4772         {
4773             nVertAdjust = -1;
4774             if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4775         }
4776         else
4777         {
4778             nVertAdjust = 1;
4779             if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4780         }
4781     }
4782
4783     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4784
4785     if (nScrollPosWidth)
4786     {
4787         INT diff = nHorzDiff / nScrollPosWidth;
4788         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4789         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4790     }
4791
4792     if (nScrollPosHeight)
4793     {
4794         INT diff = nVertDiff / nScrollPosHeight;
4795         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4796         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4797     }
4798
4799     return TRUE;
4800 }
4801
4802 /***
4803  * DESCRIPTION:
4804  * Searches for an item with specific characteristics.
4805  *
4806  * PARAMETER(S):
4807  * [I] hwnd : window handle
4808  * [I] nStart : base item index
4809  * [I] lpFindInfo : item information to look for
4810  *
4811  * RETURN:
4812  *   SUCCESS : index of item
4813  *   FAILURE : -1
4814  */
4815 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4816                               const LVFINDINFOW *lpFindInfo)
4817 {
4818     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4819     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4820     BOOL bWrap = FALSE, bNearest = FALSE;
4821     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4822     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4823     POINT Position, Destination;
4824     LVITEMW lvItem;
4825
4826     if (!lpFindInfo || nItem < 0) return -1;
4827     
4828     lvItem.mask = 0;
4829     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4830     {
4831         lvItem.mask |= LVIF_TEXT;
4832         lvItem.pszText = szDispText;
4833         lvItem.cchTextMax = DISP_TEXT_SIZE;
4834     }
4835
4836     if (lpFindInfo->flags & LVFI_WRAP)
4837         bWrap = TRUE;
4838
4839     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
4840         (uView == LVS_ICON || uView ==LVS_SMALLICON))
4841     {
4842         POINT Origin;
4843         RECT rcArea;
4844         
4845         LISTVIEW_GetOrigin(infoPtr, &Origin);
4846         Destination.x = lpFindInfo->pt.x - Origin.x;
4847         Destination.y = lpFindInfo->pt.y - Origin.y;
4848         switch(lpFindInfo->vkDirection)
4849         {
4850         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
4851         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
4852         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4853         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
4854         case VK_HOME:  Destination.x = Destination.y = 0; break;
4855         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4856         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4857         case VK_END:
4858             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4859             Destination.x = rcArea.right; 
4860             Destination.y = rcArea.bottom; 
4861             break;
4862         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4863         }
4864         bNearest = TRUE;
4865     }
4866     else Destination.x = Destination.y = 0;
4867
4868     /* if LVFI_PARAM is specified, all other flags are ignored */
4869     if (lpFindInfo->flags & LVFI_PARAM)
4870     {
4871         lvItem.mask |= LVIF_PARAM;
4872         bNearest = FALSE;
4873         lvItem.mask &= ~LVIF_TEXT;
4874     }
4875
4876 again:
4877     for (; nItem < nLast; nItem++)
4878     {
4879         lvItem.iItem = nItem;
4880         lvItem.iSubItem = 0;
4881         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4882
4883         if (lvItem.mask & LVIF_PARAM)
4884         {
4885             if (lpFindInfo->lParam == lvItem.lParam)
4886                 return nItem;
4887             else
4888                 continue;
4889         }
4890         
4891         if (lvItem.mask & LVIF_TEXT)
4892         {
4893             if (lpFindInfo->flags & LVFI_PARTIAL)
4894             {
4895                 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4896             }
4897             else
4898             {
4899                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4900             }
4901         }
4902
4903         if (!bNearest) return nItem;
4904         
4905         /* This is very inefficient. To do a good job here,
4906          * we need a sorted array of (x,y) item positions */
4907         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4908
4909         /* compute the distance^2 to the destination */
4910         xdist = Destination.x - Position.x;
4911         ydist = Destination.y - Position.y;
4912         dist = xdist * xdist + ydist * ydist;
4913
4914         /* remember the distance, and item if it's closer */
4915         if (dist < mindist)
4916         {
4917             mindist = dist;
4918             nNearestItem = nItem;
4919         }
4920     }
4921
4922     if (bWrap)
4923     {
4924         nItem = 0;
4925         nLast = min(nStart + 1, infoPtr->nItemCount);
4926         bWrap = FALSE;
4927         goto again;
4928     }
4929
4930     return nNearestItem;
4931 }
4932
4933 /***
4934  * DESCRIPTION:
4935  * Searches for an item with specific characteristics.
4936  *
4937  * PARAMETER(S):
4938  * [I] hwnd : window handle
4939  * [I] nStart : base item index
4940  * [I] lpFindInfo : item information to look for
4941  *
4942  * RETURN:
4943  *   SUCCESS : index of item
4944  *   FAILURE : -1
4945  */
4946 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4947                               const LVFINDINFOA *lpFindInfo)
4948 {
4949     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4950     LVFINDINFOW fiw;
4951     INT res;
4952
4953     memcpy(&fiw, lpFindInfo, sizeof(fiw));
4954     if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4955     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4956     if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4957     return res;
4958 }
4959
4960 /***
4961  * DESCRIPTION:
4962  * Retrieves the background image of the listview control.
4963  *
4964  * PARAMETER(S):
4965  * [I] infoPtr : valid pointer to the listview structure
4966  * [O] lpBkImage : background image attributes
4967  *
4968  * RETURN:
4969  *   SUCCESS : TRUE
4970  *   FAILURE : FALSE
4971  */
4972 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
4973 /* {   */
4974 /*   FIXME (listview, "empty stub!\n"); */
4975 /*   return FALSE;   */
4976 /* }   */
4977
4978 /***
4979  * DESCRIPTION:
4980  * Retrieves column attributes.
4981  *
4982  * PARAMETER(S):
4983  * [I] infoPtr : valid pointer to the listview structure
4984  * [I] nColumn :  column index
4985  * [IO] lpColumn : column information
4986  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4987  *           otherwise it is in fact a LPLVCOLUMNA
4988  *
4989  * RETURN:
4990  *   SUCCESS : TRUE
4991  *   FAILURE : FALSE
4992  */
4993 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4994 {
4995     COLUMN_INFO *lpColumnInfo;
4996     HDITEMW hdi;
4997
4998     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4999     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5000
5001     /* initialize memory */
5002     ZeroMemory(&hdi, sizeof(hdi));
5003
5004     if (lpColumn->mask & LVCF_TEXT)
5005     {
5006         hdi.mask |= HDI_TEXT;
5007         hdi.pszText = lpColumn->pszText;
5008         hdi.cchTextMax = lpColumn->cchTextMax;
5009     }
5010
5011     if (lpColumn->mask & LVCF_IMAGE)
5012         hdi.mask |= HDI_IMAGE;
5013
5014     if (lpColumn->mask & LVCF_ORDER)
5015         hdi.mask |= HDI_ORDER;
5016
5017     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5018
5019     if (lpColumn->mask & LVCF_FMT)
5020         lpColumn->fmt = lpColumnInfo->fmt;
5021
5022     if (lpColumn->mask & LVCF_WIDTH)
5023         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5024
5025     if (lpColumn->mask & LVCF_IMAGE)
5026         lpColumn->iImage = hdi.iImage;
5027
5028     if (lpColumn->mask & LVCF_ORDER)
5029         lpColumn->iOrder = hdi.iOrder;
5030
5031     return TRUE;
5032 }
5033
5034
5035 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5036 {
5037     INT i;
5038
5039     if (!lpiArray)
5040         return FALSE;
5041
5042     /* FIXME: little hack */
5043     for (i = 0; i < iCount; i++)
5044         lpiArray[i] = i;
5045
5046     return TRUE;
5047 }
5048
5049 /***
5050  * DESCRIPTION:
5051  * Retrieves the column width.
5052  *
5053  * PARAMETER(S):
5054  * [I] infoPtr : valid pointer to the listview structure
5055  * [I] int : column index
5056  *
5057  * RETURN:
5058  *   SUCCESS : column width
5059  *   FAILURE : zero
5060  */
5061 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5062 {
5063     INT nColumnWidth = 0;
5064     RECT rcHeader;
5065
5066     TRACE("nColumn=%d\n", nColumn);
5067
5068     /* we have a 'column' in LIST and REPORT mode only */
5069     switch(infoPtr->dwStyle & LVS_TYPEMASK)
5070     {
5071     case LVS_LIST:
5072         nColumnWidth = infoPtr->nItemWidth;
5073         break;
5074     case LVS_REPORT:
5075         if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5076         LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5077         nColumnWidth = rcHeader.right - rcHeader.left;
5078         break;
5079     }
5080
5081     TRACE("nColumnWidth=%d\n", nColumnWidth);
5082     return nColumnWidth;
5083 }
5084
5085 /***
5086  * DESCRIPTION:
5087  * In list or report display mode, retrieves the number of items that can fit
5088  * vertically in the visible area. In icon or small icon display mode,
5089  * retrieves the total number of visible items.
5090  *
5091  * PARAMETER(S):
5092  * [I] infoPtr : valid pointer to the listview structure
5093  *
5094  * RETURN:
5095  * Number of fully visible items.
5096  */
5097 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5098 {
5099     switch (infoPtr->dwStyle & LVS_TYPEMASK)
5100     {
5101     case LVS_ICON:
5102     case LVS_SMALLICON:
5103         return infoPtr->nItemCount;
5104     case LVS_REPORT:
5105         return LISTVIEW_GetCountPerColumn(infoPtr);
5106     case LVS_LIST:
5107         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5108     }
5109     assert(FALSE);
5110     return 0;
5111 }
5112
5113 /***
5114  * DESCRIPTION:
5115  * Retrieves an image list handle.
5116  *
5117  * PARAMETER(S):
5118  * [I] infoPtr : valid pointer to the listview structure
5119  * [I] nImageList : image list identifier
5120  *
5121  * RETURN:
5122  *   SUCCESS : image list handle
5123  *   FAILURE : NULL
5124  */
5125 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5126 {
5127     switch (nImageList)
5128     {
5129     case LVSIL_NORMAL: return infoPtr->himlNormal;
5130     case LVSIL_SMALL: return infoPtr->himlSmall;
5131     case LVSIL_STATE: return infoPtr->himlState;
5132     }
5133     return NULL;
5134 }
5135
5136 /* LISTVIEW_GetISearchString */
5137
5138 /***
5139  * DESCRIPTION:
5140  * Retrieves item attributes.
5141  *
5142  * PARAMETER(S):
5143  * [I] hwnd : window handle
5144  * [IO] lpLVItem : item info
5145  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5146  *           if FALSE, the lpLVItem is a LPLVITEMA.
5147  *
5148  * NOTE:
5149  *   This is the internal 'GetItem' interface -- it tries to
5150  *   be smart and avoid text copies, if possible, by modifying
5151  *   lpLVItem->pszText to point to the text string. Please note
5152  *   that this is not always possible (e.g. OWNERDATA), so on
5153  *   entry you *must* supply valid values for pszText, and cchTextMax.
5154  *   The only difference to the documented interface is that upon
5155  *   return, you should use *only* the lpLVItem->pszText, rather than
5156  *   the buffer pointer you provided on input. Most code already does
5157  *   that, so it's not a problem.
5158  *   For the two cases when the text must be copied (that is,
5159  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5160  *
5161  * RETURN:
5162  *   SUCCESS : TRUE
5163  *   FAILURE : FALSE
5164  */
5165 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5166 {
5167     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5168     NMLVDISPINFOW dispInfo;
5169     ITEM_INFO *lpItem;
5170     ITEMHDR* pItemHdr;
5171     HDPA hdpaSubItems;
5172     INT isubitem;
5173
5174     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5175
5176     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5177         return FALSE;
5178
5179     if (lpLVItem->mask == 0) return TRUE;
5180
5181     /* make a local copy */
5182     isubitem = lpLVItem->iSubItem;
5183
5184     /* a quick optimization if all we're asked is the focus state
5185      * these queries are worth optimising since they are common,
5186      * and can be answered in constant time, without the heavy accesses */
5187     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5188          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5189     {
5190         lpLVItem->state = 0;
5191         if (infoPtr->nFocusedItem == lpLVItem->iItem)
5192             lpLVItem->state |= LVIS_FOCUSED;
5193         return TRUE;
5194     }
5195
5196     ZeroMemory(&dispInfo, sizeof(dispInfo));
5197
5198     /* if the app stores all the data, handle it separately */
5199     if (infoPtr->dwStyle & LVS_OWNERDATA)
5200     {
5201         dispInfo.item.state = 0;
5202
5203         /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5204         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5205         {
5206             /* NOTE: copy only fields which we _know_ are initialized, some apps
5207              *       depend on the uninitialized fields being 0 */
5208             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5209             dispInfo.item.iItem = lpLVItem->iItem;
5210             dispInfo.item.iSubItem = isubitem;
5211             if (lpLVItem->mask & LVIF_TEXT)
5212             {
5213                 dispInfo.item.pszText = lpLVItem->pszText;
5214                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;                
5215             }
5216             if (lpLVItem->mask & LVIF_STATE)
5217                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5218             notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5219             dispInfo.item.stateMask = lpLVItem->stateMask;
5220             if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5221             {
5222                 /* full size structure expected - _WIN32IE >= 0x560 */
5223                 *lpLVItem = dispInfo.item;
5224             }
5225             else if (lpLVItem->mask & LVIF_INDENT)
5226             {
5227                 /* indent member expected - _WIN32IE >= 0x300 */
5228                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5229             }
5230             else
5231             {
5232                 /* minimal structure expected */
5233                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5234             }
5235             TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5236         }
5237         
5238         /* make sure lParam is zeroed out */
5239         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5240         
5241         /* we store only a little state, so if we're not asked, we're done */
5242         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5243
5244         /* if focus is handled by us, report it */
5245         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5246         {
5247             lpLVItem->state &= ~LVIS_FOCUSED;
5248             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5249                 lpLVItem->state |= LVIS_FOCUSED;
5250         }
5251
5252         /* and do the same for selection, if we handle it */
5253         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5254         {
5255             lpLVItem->state &= ~LVIS_SELECTED;
5256             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5257                 lpLVItem->state |= LVIS_SELECTED;
5258         }
5259         
5260         return TRUE;
5261     }
5262
5263     /* find the item and subitem structures before we proceed */
5264     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5265     lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5266     assert (lpItem);
5267
5268     if (isubitem)
5269     {
5270         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5271         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5272         if (!lpSubItem)
5273         {
5274             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5275             isubitem = 0;
5276         }
5277     }
5278     else
5279         pItemHdr = &lpItem->hdr;
5280
5281     /* Do we need to query the state from the app? */
5282     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5283     {
5284         dispInfo.item.mask |= LVIF_STATE;
5285         dispInfo.item.stateMask = infoPtr->uCallbackMask;
5286     }
5287   
5288     /* Do we need to enquire about the image? */
5289     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5290         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5291     {
5292         dispInfo.item.mask |= LVIF_IMAGE;
5293         dispInfo.item.iImage = I_IMAGECALLBACK;
5294     }
5295
5296     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5297     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5298     {
5299         dispInfo.item.mask |= LVIF_TEXT;
5300         dispInfo.item.pszText = lpLVItem->pszText;
5301         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5302         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5303             *dispInfo.item.pszText = '\0';
5304     }
5305
5306     /* If we don't have all the requested info, query the application */
5307     if (dispInfo.item.mask != 0)
5308     {
5309         dispInfo.item.iItem = lpLVItem->iItem;
5310         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5311         dispInfo.item.lParam = lpItem->lParam;
5312         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5313         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5314     }
5315
5316     /* we should not store values for subitems */
5317     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5318
5319     /* Now, handle the iImage field */
5320     if (dispInfo.item.mask & LVIF_IMAGE)
5321     {
5322         lpLVItem->iImage = dispInfo.item.iImage;
5323         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5324             pItemHdr->iImage = dispInfo.item.iImage;
5325     }
5326     else if (lpLVItem->mask & LVIF_IMAGE)
5327     {
5328         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5329             lpLVItem->iImage = pItemHdr->iImage;
5330         else
5331             lpLVItem->iImage = 0;
5332     }
5333
5334     /* The pszText field */
5335     if (dispInfo.item.mask & LVIF_TEXT)
5336     {
5337         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5338             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5339
5340         lpLVItem->pszText = dispInfo.item.pszText;
5341     }
5342     else if (lpLVItem->mask & LVIF_TEXT)
5343     {
5344         if (isW) lpLVItem->pszText = pItemHdr->pszText;
5345         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5346     }
5347
5348     /* if this is a subitem, we're done */
5349     if (isubitem) return TRUE;
5350   
5351     /* Next is the lParam field */
5352     if (dispInfo.item.mask & LVIF_PARAM)
5353     {
5354         lpLVItem->lParam = dispInfo.item.lParam;
5355         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5356             lpItem->lParam = dispInfo.item.lParam;
5357     }
5358     else if (lpLVItem->mask & LVIF_PARAM)
5359         lpLVItem->lParam = lpItem->lParam;
5360
5361     /* ... the state field (this one is different due to uCallbackmask) */
5362     if (lpLVItem->mask & LVIF_STATE) 
5363     {
5364         lpLVItem->state = lpItem->state;
5365         if (dispInfo.item.mask & LVIF_STATE)
5366         {
5367             lpLVItem->state &= ~dispInfo.item.stateMask;
5368             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5369         }
5370         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5371         {
5372             lpLVItem->state &= ~LVIS_FOCUSED;
5373             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5374                 lpLVItem->state |= LVIS_FOCUSED;
5375         }
5376         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5377         {
5378             lpLVItem->state &= ~LVIS_SELECTED;
5379             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5380                 lpLVItem->state |= LVIS_SELECTED;
5381         }           
5382     }
5383
5384     /* and last, but not least, the indent field */
5385     if (lpLVItem->mask & LVIF_INDENT)
5386         lpLVItem->iIndent = lpItem->iIndent;
5387
5388     return TRUE;
5389 }
5390
5391 /***
5392  * DESCRIPTION:
5393  * Retrieves item attributes.
5394  *
5395  * PARAMETER(S):
5396  * [I] hwnd : window handle
5397  * [IO] lpLVItem : item info
5398  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5399  *           if FALSE, the lpLVItem is a LPLVITEMA.
5400  *
5401  * NOTE:
5402  *   This is the external 'GetItem' interface -- it properly copies
5403  *   the text in the provided buffer.
5404  *
5405  * RETURN:
5406  *   SUCCESS : TRUE
5407  *   FAILURE : FALSE
5408  */
5409 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5410 {
5411     LPWSTR pszText;
5412     BOOL bResult;
5413
5414     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5415         return FALSE;
5416
5417     pszText = lpLVItem->pszText;
5418     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5419     if (bResult && lpLVItem->pszText != pszText)
5420         textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5421     lpLVItem->pszText = pszText;
5422
5423     return bResult;
5424 }
5425
5426
5427 /***
5428  * DESCRIPTION:
5429  * Retrieves the position (upper-left) of the listview control item.
5430  * Note that for LVS_ICON style, the upper-left is that of the icon
5431  * and not the bounding box.
5432  *
5433  * PARAMETER(S):
5434  * [I] infoPtr : valid pointer to the listview structure
5435  * [I] nItem : item index
5436  * [O] lpptPosition : coordinate information
5437  *
5438  * RETURN:
5439  *   SUCCESS : TRUE
5440  *   FAILURE : FALSE
5441  */
5442 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5443 {
5444     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5445     POINT Origin;
5446
5447     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5448
5449     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5450
5451     LISTVIEW_GetOrigin(infoPtr, &Origin);
5452     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5453
5454     if (uView == LVS_ICON)
5455     {
5456         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5457         lpptPosition->y += ICON_TOP_PADDING;
5458     }
5459     lpptPosition->x += Origin.x;
5460     lpptPosition->y += Origin.y;
5461     
5462     TRACE ("  lpptPosition=%s\n", debugpoint(lpptPosition));
5463     return TRUE;
5464 }
5465
5466
5467 /***
5468  * DESCRIPTION:
5469  * Retrieves the bounding rectangle for a listview control item.
5470  *
5471  * PARAMETER(S):
5472  * [I] infoPtr : valid pointer to the listview structure
5473  * [I] nItem : item index
5474  * [IO] lprc : bounding rectangle coordinates
5475  *     lprc->left specifies the portion of the item for which the bounding
5476  *     rectangle will be retrieved.
5477  *
5478  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5479  *        including the icon and label.
5480  *         *
5481  *         * For LVS_ICON
5482  *         * Experiment shows that native control returns:
5483  *         *  width = min (48, length of text line)
5484  *         *    .left = position.x - (width - iconsize.cx)/2
5485  *         *    .right = .left + width
5486  *         *  height = #lines of text * ntmHeight + icon height + 8
5487  *         *    .top = position.y - 2
5488  *         *    .bottom = .top + height
5489  *         *  separation between items .y = itemSpacing.cy - height
5490  *         *                           .x = itemSpacing.cx - width
5491  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5492  *         *
5493  *         * For LVS_ICON
5494  *         * Experiment shows that native control returns:
5495  *         *  width = iconSize.cx + 16
5496  *         *    .left = position.x - (width - iconsize.cx)/2
5497  *         *    .right = .left + width
5498  *         *  height = iconSize.cy + 4
5499  *         *    .top = position.y - 2
5500  *         *    .bottom = .top + height
5501  *         *  separation between items .y = itemSpacing.cy - height
5502  *         *                           .x = itemSpacing.cx - width
5503  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5504  *         *
5505  *         * For LVS_ICON
5506  *         * Experiment shows that native control returns:
5507  *         *  width = text length
5508  *         *    .left = position.x - width/2
5509  *         *    .right = .left + width
5510  *         *  height = ntmH * linecount + 2
5511  *         *    .top = position.y + iconSize.cy + 6
5512  *         *    .bottom = .top + height
5513  *         *  separation between items .y = itemSpacing.cy - height
5514  *         *                           .x = itemSpacing.cx - width
5515  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5516  *      rectangles, but excludes columns in report view.
5517  *
5518  * RETURN:
5519  *   SUCCESS : TRUE
5520  *   FAILURE : FALSE
5521  *
5522  * NOTES
5523  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5524  *   upon whether the window has the focus currently and on whether the item
5525  *   is the one with the focus.  Ensure that the control's record of which
5526  *   item has the focus agrees with the items' records.
5527  */
5528 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5529 {
5530     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5531     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5532     BOOL doLabel = TRUE, oversizedBox = FALSE;
5533     POINT Position, Origin;
5534     LVITEMW lvItem;
5535     RECT label_rect;
5536
5537     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5538
5539     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5540
5541     LISTVIEW_GetOrigin(infoPtr, &Origin);
5542     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5543
5544     /* Be smart and try to figure out the minimum we have to do */
5545     if (lprc->left == LVIR_ICON) doLabel = FALSE;
5546     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5547     if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5548         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5549         oversizedBox = TRUE;
5550
5551     /* get what we need from the item before hand, so we make
5552      * only one request. This can speed up things, if data
5553      * is stored on the app side */
5554     lvItem.mask = 0;
5555     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5556     if (doLabel) lvItem.mask |= LVIF_TEXT;
5557     lvItem.iItem = nItem;
5558     lvItem.iSubItem = 0;
5559     lvItem.pszText = szDispText;
5560     lvItem.cchTextMax = DISP_TEXT_SIZE;
5561     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5562     /* we got the state already up, simulate it here, to avoid a reget */
5563     if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5564     {
5565         lvItem.mask |= LVIF_STATE;
5566         lvItem.stateMask = LVIS_FOCUSED;
5567         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5568     }
5569
5570     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5571         lprc->left = LVIR_BOUNDS;
5572     switch(lprc->left)
5573     {
5574     case LVIR_ICON:
5575         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5576         break;
5577
5578     case LVIR_LABEL:
5579         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5580         break;
5581
5582     case LVIR_BOUNDS:
5583         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5584         break;
5585
5586     case LVIR_SELECTBOUNDS:
5587         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5588         UnionRect(lprc, lprc, &label_rect);
5589         break;
5590
5591     default:
5592         WARN("Unknown value: %ld\n", lprc->left);
5593         return FALSE;
5594     }
5595
5596     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5597
5598     TRACE(" rect=%s\n", debugrect(lprc));
5599
5600     return TRUE;
5601 }
5602
5603 /***
5604  * DESCRIPTION:
5605  * Retrieves the spacing between listview control items.
5606  *
5607  * PARAMETER(S):
5608  * [I] infoPtr : valid pointer to the listview structure
5609  * [IO] lprc : rectangle to receive the output
5610  *             on input, lprc->top = nSubItem
5611  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5612  * 
5613  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5614  *       not only those of the first column.
5615  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5616  * 
5617  * RETURN:
5618  *     TRUE: success
5619  *     FALSE: failure
5620  */
5621 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5622 {
5623     POINT Position;
5624     LVITEMW lvItem;
5625     INT nColumn = lprc->top;
5626     
5627     if (!lprc) return FALSE;
5628
5629     TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5630     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5631     if (lprc->top == 0)
5632         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5633
5634     if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5635
5636     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5637
5638     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5639
5640     lvItem.mask = 0;
5641     lvItem.iItem = nItem;
5642     lvItem.iSubItem = nColumn;
5643     
5644     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5645     switch(lprc->left)
5646     {
5647     case LVIR_ICON:
5648         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5649         break;
5650
5651     case LVIR_LABEL:
5652     case LVIR_BOUNDS:
5653         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5654         break;
5655
5656     default:
5657         ERR("Unknown bounds=%ld\n", lprc->left);
5658         return FALSE;
5659     }
5660
5661     OffsetRect(lprc, Position.x, Position.y);
5662     return TRUE;
5663 }
5664
5665
5666 /***
5667  * DESCRIPTION:
5668  * Retrieves the width of a label.
5669  *
5670  * PARAMETER(S):
5671  * [I] infoPtr : valid pointer to the listview structure
5672  *
5673  * RETURN:
5674  *   SUCCESS : string width (in pixels)
5675  *   FAILURE : zero
5676  */
5677 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5678 {
5679     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5680     LVITEMW lvItem;
5681
5682     TRACE("(nItem=%d)\n", nItem);
5683
5684     lvItem.mask = LVIF_TEXT;
5685     lvItem.iItem = nItem;
5686     lvItem.iSubItem = 0;
5687     lvItem.pszText = szDispText;
5688     lvItem.cchTextMax = DISP_TEXT_SIZE;
5689     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5690   
5691     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5692 }
5693
5694 /***
5695  * DESCRIPTION:
5696  * Retrieves the spacing between listview control items.
5697  *
5698  * PARAMETER(S):
5699  * [I] infoPtr : valid pointer to the listview structure
5700  * [I] bSmall : flag for small or large icon
5701  *
5702  * RETURN:
5703  * Horizontal + vertical spacing
5704  */
5705 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5706 {
5707   LONG lResult;
5708
5709   if (!bSmall)
5710   {
5711     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5712   }
5713   else
5714   {
5715     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5716       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5717     else
5718       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5719   }
5720   return lResult;
5721 }
5722
5723 /***
5724  * DESCRIPTION:
5725  * Retrieves the state of a listview control item.
5726  *
5727  * PARAMETER(S):
5728  * [I] infoPtr : valid pointer to the listview structure
5729  * [I] nItem : item index
5730  * [I] uMask : state mask
5731  *
5732  * RETURN:
5733  * State specified by the mask.
5734  */
5735 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5736 {
5737     LVITEMW lvItem;
5738
5739     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5740
5741     lvItem.iItem = nItem;
5742     lvItem.iSubItem = 0;
5743     lvItem.mask = LVIF_STATE;
5744     lvItem.stateMask = uMask;
5745     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5746
5747     return lvItem.state & uMask;
5748 }
5749
5750 /***
5751  * DESCRIPTION:
5752  * Retrieves the text of a listview control item or subitem.
5753  *
5754  * PARAMETER(S):
5755  * [I] hwnd : window handle
5756  * [I] nItem : item index
5757  * [IO] lpLVItem : item information
5758  * [I] isW :  TRUE if lpLVItem is Unicode
5759  *
5760  * RETURN:
5761  *   SUCCESS : string length
5762  *   FAILURE : 0
5763  */
5764 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5765 {
5766     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5767
5768     lpLVItem->mask = LVIF_TEXT;
5769     lpLVItem->iItem = nItem;
5770     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5771
5772     return textlenT(lpLVItem->pszText, isW);
5773 }
5774
5775 /***
5776  * DESCRIPTION:
5777  * Searches for an item based on properties + relationships.
5778  *
5779  * PARAMETER(S):
5780  * [I] infoPtr : valid pointer to the listview structure
5781  * [I] nItem : item index
5782  * [I] uFlags : relationship flag
5783  *
5784  * RETURN:
5785  *   SUCCESS : item index
5786  *   FAILURE : -1
5787  */
5788 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5789 {
5790     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5791     UINT uMask = 0;
5792     LVFINDINFOW lvFindInfo;
5793     INT nCountPerColumn;
5794     INT nCountPerRow;
5795     INT i;
5796
5797     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5798     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5799
5800     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5801
5802     if (uFlags & LVNI_CUT)
5803       uMask |= LVIS_CUT;
5804
5805     if (uFlags & LVNI_DROPHILITED)
5806       uMask |= LVIS_DROPHILITED;
5807
5808     if (uFlags & LVNI_FOCUSED)
5809       uMask |= LVIS_FOCUSED;
5810
5811     if (uFlags & LVNI_SELECTED)
5812       uMask |= LVIS_SELECTED;
5813
5814     /* if we're asked for the focused item, that's only one, 
5815      * so it's worth optimizing */
5816     if (uFlags & LVNI_FOCUSED)
5817     {
5818         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5819         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5820     }
5821     
5822     if (uFlags & LVNI_ABOVE)
5823     {
5824       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5825       {
5826         while (nItem >= 0)
5827         {
5828           nItem--;
5829           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5830             return nItem;
5831         }
5832       }
5833       else
5834       {
5835         /* Special case for autoarrange - move 'til the top of a list */
5836         if (is_autoarrange(infoPtr))
5837         {
5838           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5839           while (nItem - nCountPerRow >= 0)
5840           {
5841             nItem -= nCountPerRow;
5842             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5843               return nItem;
5844           }
5845           return -1;
5846         }
5847         lvFindInfo.flags = LVFI_NEARESTXY;
5848         lvFindInfo.vkDirection = VK_UP;
5849         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5850         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5851         {
5852           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5853             return nItem;
5854         }
5855       }
5856     }
5857     else if (uFlags & LVNI_BELOW)
5858     {
5859       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5860       {
5861         while (nItem < infoPtr->nItemCount)
5862         {
5863           nItem++;
5864           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5865             return nItem;
5866         }
5867       }
5868       else
5869       {
5870         /* Special case for autoarrange - move 'til the bottom of a list */
5871         if (is_autoarrange(infoPtr))
5872         {
5873           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5874           while (nItem + nCountPerRow < infoPtr->nItemCount )
5875           {
5876             nItem += nCountPerRow;
5877             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5878               return nItem;
5879           }
5880           return -1;
5881         }
5882         lvFindInfo.flags = LVFI_NEARESTXY;
5883         lvFindInfo.vkDirection = VK_DOWN;
5884         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5885         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5886         {
5887           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5888             return nItem;
5889         }
5890       }
5891     }
5892     else if (uFlags & LVNI_TOLEFT)
5893     {
5894       if (uView == LVS_LIST)
5895       {
5896         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5897         while (nItem - nCountPerColumn >= 0)
5898         {
5899           nItem -= nCountPerColumn;
5900           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5901             return nItem;
5902         }
5903       }
5904       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5905       {
5906         /* Special case for autoarrange - move 'ti the beginning of a row */
5907         if (is_autoarrange(infoPtr))
5908         {
5909           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5910           while (nItem % nCountPerRow > 0)
5911           {
5912             nItem --;
5913             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5914               return nItem;
5915           }
5916           return -1;
5917         }
5918         lvFindInfo.flags = LVFI_NEARESTXY;
5919         lvFindInfo.vkDirection = VK_LEFT;
5920         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5921         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5922         {
5923           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5924             return nItem;
5925         }
5926       }
5927     }
5928     else if (uFlags & LVNI_TORIGHT)
5929     {
5930       if (uView == LVS_LIST)
5931       {
5932         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5933         while (nItem + nCountPerColumn < infoPtr->nItemCount)
5934         {
5935           nItem += nCountPerColumn;
5936           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5937             return nItem;
5938         }
5939       }
5940       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5941       {
5942         /* Special case for autoarrange - move 'til the end of a row */
5943         if (is_autoarrange(infoPtr))
5944         {
5945           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5946           while (nItem % nCountPerRow < nCountPerRow - 1 )
5947           {
5948             nItem ++;
5949             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5950               return nItem;
5951           }
5952           return -1;
5953         }
5954         lvFindInfo.flags = LVFI_NEARESTXY;
5955         lvFindInfo.vkDirection = VK_RIGHT;
5956         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5957         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5958         {
5959           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5960             return nItem;
5961         }
5962       }
5963     }
5964     else
5965     {
5966       nItem++;
5967
5968       /* search by index */
5969       for (i = nItem; i < infoPtr->nItemCount; i++)
5970       {
5971         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5972           return i;
5973       }
5974     }
5975
5976     return -1;
5977 }
5978
5979 /* LISTVIEW_GetNumberOfWorkAreas */
5980
5981 /***
5982  * DESCRIPTION:
5983  * Retrieves the origin coordinates when in icon or small icon display mode.
5984  *
5985  * PARAMETER(S):
5986  * [I] infoPtr : valid pointer to the listview structure
5987  * [O] lpptOrigin : coordinate information
5988  *
5989  * RETURN:
5990  *   None.
5991  */
5992 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5993 {
5994     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5995     INT nHorzPos = 0, nVertPos = 0;
5996     SCROLLINFO scrollInfo;
5997
5998     scrollInfo.cbSize = sizeof(SCROLLINFO);    
5999     scrollInfo.fMask = SIF_POS;
6000     
6001     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6002         nHorzPos = scrollInfo.nPos;
6003     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6004         nVertPos = scrollInfo.nPos;
6005
6006     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6007
6008     lpptOrigin->x = infoPtr->rcList.left;
6009     lpptOrigin->y = infoPtr->rcList.top;
6010     if (uView == LVS_LIST)
6011         nHorzPos *= infoPtr->nItemWidth;
6012     else if (uView == LVS_REPORT)
6013         nVertPos *= infoPtr->nItemHeight;
6014     
6015     lpptOrigin->x -= nHorzPos;
6016     lpptOrigin->y -= nVertPos;
6017
6018     TRACE(" origin=%s\n", debugpoint(lpptOrigin));
6019 }
6020
6021 /***
6022  * DESCRIPTION:
6023  * Retrieves the width of a string.
6024  *
6025  * PARAMETER(S):
6026  * [I] hwnd : window handle
6027  * [I] lpszText : text string to process
6028  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6029  *
6030  * RETURN:
6031  *   SUCCESS : string width (in pixels)
6032  *   FAILURE : zero
6033  */
6034 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6035 {
6036     SIZE stringSize;
6037     
6038     stringSize.cx = 0;    
6039     if (is_textT(lpszText, isW))
6040     {
6041         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6042         HDC hdc = GetDC(infoPtr->hwndSelf);
6043         HFONT hOldFont = SelectObject(hdc, hFont);
6044
6045         if (isW)
6046             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6047         else
6048             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6049         SelectObject(hdc, hOldFont);
6050         ReleaseDC(infoPtr->hwndSelf, hdc);
6051     }
6052     return stringSize.cx;
6053 }
6054
6055 /***
6056  * DESCRIPTION:
6057  * Determines which listview item is located at the specified position.
6058  *
6059  * PARAMETER(S):
6060  * [I] infoPtr : valid pointer to the listview structure
6061  * [IO] lpht : hit test information
6062  * [I] subitem : fill out iSubItem.
6063  * [I] select : return the index only if the hit selects the item
6064  *
6065  * NOTE:
6066  * (mm 20001022): We must not allow iSubItem to be touched, for
6067  * an app might pass only a structure with space up to iItem!
6068  * (MS Office 97 does that for instance in the file open dialog)
6069  * 
6070  * RETURN:
6071  *   SUCCESS : item index
6072  *   FAILURE : -1
6073  */
6074 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6075 {
6076     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6077     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6078     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6079     POINT Origin, Position, opt;
6080     LVITEMW lvItem;
6081     ITERATOR i;
6082     INT iItem;
6083     
6084     TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
6085     
6086     lpht->flags = 0;
6087     lpht->iItem = -1;
6088     if (subitem) lpht->iSubItem = 0;
6089
6090     if (infoPtr->rcList.left > lpht->pt.x)
6091         lpht->flags |= LVHT_TOLEFT;
6092     else if (infoPtr->rcList.right < lpht->pt.x)
6093         lpht->flags |= LVHT_TORIGHT;
6094     
6095     if (infoPtr->rcList.top > lpht->pt.y)
6096         lpht->flags |= LVHT_ABOVE;
6097     else if (infoPtr->rcList.bottom < lpht->pt.y)
6098         lpht->flags |= LVHT_BELOW;
6099
6100     TRACE("lpht->flags=0x%x\n", lpht->flags);
6101     if (lpht->flags) return -1;
6102
6103     lpht->flags |= LVHT_NOWHERE;
6104
6105     LISTVIEW_GetOrigin(infoPtr, &Origin);
6106    
6107     /* first deal with the large items */
6108     rcSearch.left = lpht->pt.x;
6109     rcSearch.top = lpht->pt.y;
6110     rcSearch.right = rcSearch.left + 1;
6111     rcSearch.bottom = rcSearch.top + 1;
6112     
6113     iterator_frameditems(&i, infoPtr, &rcSearch);
6114     iterator_next(&i); /* go to first item in the sequence */
6115     iItem = i.nItem;
6116     iterator_destroy(&i);
6117    
6118     TRACE("lpht->iItem=%d\n", iItem); 
6119     if (iItem == -1) return -1;
6120
6121     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6122     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6123     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6124     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6125     lvItem.iItem = iItem;
6126     lvItem.iSubItem = 0;
6127     lvItem.pszText = szDispText;
6128     lvItem.cchTextMax = DISP_TEXT_SIZE;
6129     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6130     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED; 
6131     
6132     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6133     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6134     opt.x = lpht->pt.x - Position.x - Origin.x;
6135     opt.y = lpht->pt.y - Position.y - Origin.y;
6136     
6137     if (uView == LVS_REPORT)
6138         rcBounds = rcBox;
6139     else
6140         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6141     TRACE("rcBounds=%s\n", debugrect(&rcBounds));
6142     if (!PtInRect(&rcBounds, opt)) return -1;
6143
6144     if (PtInRect(&rcIcon, opt))
6145         lpht->flags |= LVHT_ONITEMICON;
6146     else if (PtInRect(&rcLabel, opt))
6147         lpht->flags |= LVHT_ONITEMLABEL;
6148     else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6149         lpht->flags |= LVHT_ONITEMSTATEICON;
6150     if (lpht->flags & LVHT_ONITEM)
6151         lpht->flags &= ~LVHT_NOWHERE;
6152    
6153     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6154     if (uView == LVS_REPORT && subitem)
6155     {
6156         INT j;
6157
6158         rcBounds.right = rcBounds.left;
6159         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6160         {
6161             rcBounds.left = rcBounds.right;
6162             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6163             if (PtInRect(&rcBounds, opt))
6164             {
6165                 lpht->iSubItem = j;
6166                 break;
6167             }
6168         }
6169     }
6170
6171     if (select && !(uView == LVS_REPORT &&
6172                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6173                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6174     {
6175         if (uView == LVS_REPORT)
6176         {
6177             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6178             UnionRect(&rcBounds, &rcBounds, &rcState);
6179         }
6180         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6181     }
6182     return lpht->iItem = iItem;
6183 }
6184
6185
6186 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6187    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6188    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6189    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6190    their own sort proc. when sending LVM_SORTITEMS.
6191 */
6192 /* Platform SDK:
6193     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6194         if:
6195           LVS_SORTXXX must be specified,
6196           LVS_OWNERDRAW is not set,
6197           <item>.pszText is not LPSTR_TEXTCALLBACK.
6198
6199     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6200     are sorted based on item text..."
6201 */
6202 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6203 {
6204     ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6205     ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6206     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
6207
6208     /* if we're sorting descending, negate the return value */
6209     return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6210 }
6211
6212 /***
6213  * DESCRIPTION:
6214  * Inserts a new item in the listview control.
6215  *
6216  * PARAMETER(S):
6217  * [I] infoPtr : valid pointer to the listview structure
6218  * [I] lpLVItem : item information
6219  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6220  *
6221  * RETURN:
6222  *   SUCCESS : new item index
6223  *   FAILURE : -1
6224  */
6225 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6226 {
6227     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6228     INT nItem;
6229     HDPA hdpaSubItems;
6230     NMLISTVIEW nmlv;
6231     ITEM_INFO *lpItem;
6232     BOOL is_sorted, has_changed;
6233     LVITEMW item;
6234     HWND hwndSelf = infoPtr->hwndSelf;
6235
6236     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6237
6238     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6239
6240     /* make sure it's an item, and not a subitem; cannot insert a subitem */
6241     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6242
6243     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6244
6245     if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6246     
6247     /* insert item in listview control data structure */
6248     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6249     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6250
6251     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6252                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6253
6254     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6255     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6256     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6257     if (nItem == -1) goto fail;
6258     infoPtr->nItemCount++;
6259
6260     /* shift indices first so they don't get tangled */
6261     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6262
6263     /* set the item attributes */
6264     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6265     {
6266         /* full size structure expected - _WIN32IE >= 0x560 */
6267         item = *lpLVItem;
6268     }
6269     else if (lpLVItem->mask & LVIF_INDENT)
6270     {
6271         /* indent member expected - _WIN32IE >= 0x300 */
6272         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6273     }
6274     else
6275     {
6276         /* minimal structure expected */
6277         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6278     }
6279     item.iItem = nItem;
6280     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6281     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6282
6283     /* if we're sorted, sort the list, and update the index */
6284     if (is_sorted)
6285     {
6286         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6287         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6288         assert(nItem != -1);
6289     }
6290
6291     /* make room for the position, if we are in the right mode */
6292     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6293     {
6294         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6295             goto undo;
6296         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6297         {
6298             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6299             goto undo;
6300         }
6301     }
6302     
6303     /* send LVN_INSERTITEM notification */
6304     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6305     nmlv.iItem = nItem;
6306     nmlv.lParam = lpItem->lParam;
6307     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6308     if (!IsWindow(hwndSelf))
6309         return -1;
6310
6311     /* align items (set position of each item) */
6312     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6313     {
6314         POINT pt;
6315
6316         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6317             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6318         else
6319             LISTVIEW_NextIconPosTop(infoPtr, &pt);
6320
6321         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6322     }
6323
6324     /* now is the invalidation fun */
6325     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6326     return nItem;
6327
6328 undo:
6329     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6330     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6331     infoPtr->nItemCount--;
6332 fail:
6333     DPA_DeletePtr(hdpaSubItems, 0);
6334     DPA_Destroy (hdpaSubItems);
6335     Free (lpItem);
6336     return -1;
6337 }
6338
6339 /***
6340  * DESCRIPTION:
6341  * Redraws a range of items.
6342  *
6343  * PARAMETER(S):
6344  * [I] infoPtr : valid pointer to the listview structure
6345  * [I] nFirst : first item
6346  * [I] nLast : last item
6347  *
6348  * RETURN:
6349  *   SUCCESS : TRUE
6350  *   FAILURE : FALSE
6351  */
6352 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6353 {
6354     INT i;
6355  
6356     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
6357         max(nFirst, nLast) >= infoPtr->nItemCount)
6358         return FALSE;
6359     
6360     for (i = nFirst; i <= nLast; i++)
6361         LISTVIEW_InvalidateItem(infoPtr, i);
6362
6363     return TRUE;
6364 }
6365
6366 /***
6367  * DESCRIPTION:
6368  * Scroll the content of a listview.
6369  *
6370  * PARAMETER(S):
6371  * [I] infoPtr : valid pointer to the listview structure
6372  * [I] dx : horizontal scroll amount in pixels
6373  * [I] dy : vertical scroll amount in pixels
6374  *
6375  * RETURN:
6376  *   SUCCESS : TRUE
6377  *   FAILURE : FALSE
6378  *
6379  * COMMENTS:
6380  *  If the control is in report mode (LVS_REPORT) the control can
6381  *  be scrolled only in line increments. "dy" will be rounded to the
6382  *  nearest number of pixels that are a whole line. Ex: if line height
6383  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6384  *  is passed the the scroll will be 0.  (per MSDN 7/2002)
6385  *
6386  *  For:  (per experimentaion with native control and CSpy ListView)
6387  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
6388  *                    dx ignored
6389  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
6390  *                    dx ignored
6391  *     LVS_LIST       dx=1 = 1 column (horizontal only)
6392  *                           but will only scroll 1 column per message
6393  *                           no matter what the value.
6394  *                    dy must be 0 or FALSE returned.
6395  *     LVS_REPORT     dx=1 = 1 pixel
6396  *                    dy=  see above
6397  *
6398  */
6399 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6400 {
6401     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6402     case LVS_REPORT:
6403         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6404         dy /= infoPtr->nItemHeight;
6405         break;
6406     case LVS_LIST:
6407         if (dy != 0) return FALSE;
6408         break;
6409     default: /* icon */
6410         dx = 0;
6411         break;
6412     }   
6413
6414     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6415     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6416   
6417     return TRUE;
6418 }
6419
6420 /***
6421  * DESCRIPTION:
6422  * Sets the background color.
6423  *
6424  * PARAMETER(S):
6425  * [I] infoPtr : valid pointer to the listview structure
6426  * [I] clrBk : background color
6427  *
6428  * RETURN:
6429  *   SUCCESS : TRUE
6430  *   FAILURE : FALSE
6431  */
6432 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6433 {
6434     TRACE("(clrBk=%lx)\n", clrBk);
6435
6436     if(infoPtr->clrBk != clrBk) {
6437         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6438         infoPtr->clrBk = clrBk;
6439         if (clrBk == CLR_NONE)
6440             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6441         else
6442             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6443         LISTVIEW_InvalidateList(infoPtr);
6444     }
6445
6446    return TRUE;
6447 }
6448
6449 /* LISTVIEW_SetBkImage */
6450
6451 /*** Helper for {Insert,Set}ColumnT *only* */
6452 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6453 {
6454     if (lpColumn->mask & LVCF_FMT)
6455     {
6456         /* format member is valid */
6457         lphdi->mask |= HDI_FORMAT;
6458
6459         /* set text alignment (leftmost column must be left-aligned) */
6460         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6461             lphdi->fmt |= HDF_LEFT;
6462         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6463             lphdi->fmt |= HDF_RIGHT;
6464         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6465             lphdi->fmt |= HDF_CENTER;
6466
6467         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6468             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6469
6470         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6471         {
6472             lphdi->fmt |= HDF_IMAGE;
6473             lphdi->iImage = I_IMAGECALLBACK;
6474         }
6475     }
6476
6477     if (lpColumn->mask & LVCF_WIDTH)
6478     {
6479         lphdi->mask |= HDI_WIDTH;
6480         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6481         {
6482             /* make it fill the remainder of the controls width */
6483             RECT rcHeader;
6484             INT item_index;
6485
6486             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6487             {
6488                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6489                 lphdi->cxy += rcHeader.right - rcHeader.left;
6490             }
6491
6492             /* retrieve the layout of the header */
6493             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6494             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6495
6496             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6497         }
6498         else
6499             lphdi->cxy = lpColumn->cx;
6500     }
6501
6502     if (lpColumn->mask & LVCF_TEXT)
6503     {
6504         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6505         lphdi->fmt |= HDF_STRING;
6506         lphdi->pszText = lpColumn->pszText;
6507         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6508     }
6509
6510     if (lpColumn->mask & LVCF_IMAGE)
6511     {
6512         lphdi->mask |= HDI_IMAGE;
6513         lphdi->iImage = lpColumn->iImage;
6514     }
6515
6516     if (lpColumn->mask & LVCF_ORDER)
6517     {
6518         lphdi->mask |= HDI_ORDER;
6519         lphdi->iOrder = lpColumn->iOrder;
6520     }
6521 }
6522
6523
6524 /***
6525  * DESCRIPTION:
6526  * Inserts a new column.
6527  *
6528  * PARAMETER(S):
6529  * [I] infoPtr : valid pointer to the listview structure
6530  * [I] nColumn : column index
6531  * [I] lpColumn : column information
6532  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6533  *
6534  * RETURN:
6535  *   SUCCESS : new column index
6536  *   FAILURE : -1
6537  */
6538 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6539                                   const LVCOLUMNW *lpColumn, BOOL isW)
6540 {
6541     COLUMN_INFO *lpColumnInfo;
6542     INT nNewColumn;
6543     HDITEMW hdi;
6544
6545     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6546
6547     if (!lpColumn || nColumn < 0) return -1;
6548     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6549     
6550     ZeroMemory(&hdi, sizeof(HDITEMW));
6551     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6552
6553     /* insert item in header control */
6554     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6555                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6556                               (WPARAM)nColumn, (LPARAM)&hdi);
6557     if (nNewColumn == -1) return -1;
6558     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6559    
6560     /* create our own column info */ 
6561     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6562     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6563
6564     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6565     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6566
6567     /* now we have to actually adjust the data */
6568     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6569     {
6570         SUBITEM_INFO *lpSubItem;
6571         HDPA hdpaSubItems;
6572         INT nItem, i;
6573         
6574         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6575         {
6576             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6577             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6578             {
6579                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6580                 if (lpSubItem->iSubItem >= nNewColumn)
6581                     lpSubItem->iSubItem++;
6582             }
6583         }
6584     }
6585
6586     /* make space for the new column */
6587     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6588     
6589     return nNewColumn;
6590
6591 fail:
6592     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6593     if (lpColumnInfo)
6594     {
6595         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6596         Free(lpColumnInfo);
6597     }
6598     return -1;
6599 }
6600
6601 /***
6602  * DESCRIPTION:
6603  * Sets the attributes of a header item.
6604  *
6605  * PARAMETER(S):
6606  * [I] infoPtr : valid pointer to the listview structure
6607  * [I] nColumn : column index
6608  * [I] lpColumn : column attributes
6609  * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6610  *
6611  * RETURN:
6612  *   SUCCESS : TRUE
6613  *   FAILURE : FALSE
6614  */
6615 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6616                                 const LVCOLUMNW *lpColumn, BOOL isW)
6617 {
6618     HDITEMW hdi, hdiget;
6619     BOOL bResult;
6620
6621     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6622     
6623     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6624
6625     ZeroMemory(&hdi, sizeof(HDITEMW));
6626     if (lpColumn->mask & LVCF_FMT)
6627     {
6628         hdi.mask |= HDI_FORMAT;
6629         hdiget.mask = HDI_FORMAT;
6630         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6631             hdi.fmt = hdiget.fmt & HDF_STRING;
6632     }
6633     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6634
6635     /* set header item attributes */
6636     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6637     if (!bResult) return FALSE;
6638
6639     if (lpColumn->mask & LVCF_FMT)
6640     {
6641         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6642         int oldFmt = lpColumnInfo->fmt;
6643         
6644         lpColumnInfo->fmt = lpColumn->fmt;
6645         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6646         {
6647             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6648             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6649         }
6650     }
6651
6652     return TRUE;
6653 }
6654
6655 /***
6656  * DESCRIPTION:
6657  * Sets the column order array
6658  *
6659  * PARAMETERS:
6660  * [I] infoPtr : valid pointer to the listview structure
6661  * [I] iCount : number of elements in column order array
6662  * [I] lpiArray : pointer to column order array
6663  *
6664  * RETURN:
6665  *   SUCCESS : TRUE
6666  *   FAILURE : FALSE
6667  */
6668 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6669 {
6670   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6671
6672   if (!lpiArray)
6673     return FALSE;
6674
6675   return TRUE;
6676 }
6677
6678
6679 /***
6680  * DESCRIPTION:
6681  * Sets the width of a column
6682  *
6683  * PARAMETERS:
6684  * [I] infoPtr : valid pointer to the listview structure
6685  * [I] nColumn : column index
6686  * [I] cx : column width
6687  *
6688  * RETURN:
6689  *   SUCCESS : TRUE
6690  *   FAILURE : FALSE
6691  */
6692 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6693 {
6694     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6695     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6696     INT max_cx = 0;
6697     HDITEMW hdi;
6698
6699     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6700
6701     /* set column width only if in report or list mode */
6702     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6703
6704     /* take care of invalid cx values */
6705     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6706     else if (uView == LVS_LIST && cx < 1) return FALSE;
6707
6708     /* resize all columns if in LVS_LIST mode */
6709     if(uView == LVS_LIST) 
6710     {
6711         infoPtr->nItemWidth = cx;
6712         LISTVIEW_InvalidateList(infoPtr);
6713         return TRUE;
6714     }
6715
6716     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6717
6718     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6719     {
6720         INT nLabelWidth;
6721         LVITEMW lvItem;
6722
6723         lvItem.mask = LVIF_TEXT;        
6724         lvItem.iItem = 0;
6725         lvItem.iSubItem = nColumn;
6726         lvItem.pszText = szDispText;
6727         lvItem.cchTextMax = DISP_TEXT_SIZE;
6728         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6729         {
6730             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6731             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6732             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6733         }
6734         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6735             max_cx += infoPtr->iconSize.cx;
6736         max_cx += TRAILING_LABEL_PADDING;
6737     }
6738
6739     /* autosize based on listview items width */
6740     if(cx == LVSCW_AUTOSIZE)
6741         cx = max_cx;
6742     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6743     {
6744         /* if iCol is the last column make it fill the remainder of the controls width */
6745         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
6746         {
6747             RECT rcHeader;
6748             POINT Origin;
6749
6750             LISTVIEW_GetOrigin(infoPtr, &Origin);
6751             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6752
6753             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6754         }
6755         else
6756         {
6757             /* Despite what the MS docs say, if this is not the last
6758                column, then MS resizes the column to the width of the
6759                largest text string in the column, including headers
6760                and items. This is different from LVSCW_AUTOSIZE in that
6761                LVSCW_AUTOSIZE ignores the header string length. */
6762             cx = 0;
6763
6764             /* retrieve header text */
6765             hdi.mask = HDI_TEXT;
6766             hdi.cchTextMax = DISP_TEXT_SIZE;
6767             hdi.pszText = szDispText;
6768             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6769             {
6770                 HDC hdc = GetDC(infoPtr->hwndSelf);
6771                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6772                 SIZE size;
6773
6774                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6775                     cx = size.cx + TRAILING_HEADER_PADDING;
6776                 /* FIXME: Take into account the header image, if one is present */
6777                 SelectObject(hdc, old_font);
6778                 ReleaseDC(infoPtr->hwndSelf, hdc);
6779             }
6780             cx = max (cx, max_cx);
6781         }
6782     }
6783
6784     if (cx < 0) return FALSE;
6785
6786     /* call header to update the column change */
6787     hdi.mask = HDI_WIDTH;
6788     hdi.cxy = cx;
6789     TRACE("hdi.cxy=%d\n", hdi.cxy);
6790     return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6791 }
6792
6793 /***
6794  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
6795  *
6796  */
6797 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6798 {
6799     HDC hdc_wnd, hdc;
6800     HBITMAP hbm_im, hbm_mask, hbm_orig;
6801     RECT rc;
6802     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6803     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6804     HIMAGELIST himl;
6805
6806     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6807                             ILC_COLOR | ILC_MASK, 2, 2);
6808     hdc_wnd = GetDC(infoPtr->hwndSelf);
6809     hdc = CreateCompatibleDC(hdc_wnd);
6810     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6811     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6812     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6813
6814     rc.left = rc.top = 0;
6815     rc.right = GetSystemMetrics(SM_CXSMICON);
6816     rc.bottom = GetSystemMetrics(SM_CYSMICON);
6817
6818     hbm_orig = SelectObject(hdc, hbm_mask);
6819     FillRect(hdc, &rc, hbr_white);
6820     InflateRect(&rc, -3, -3);
6821     FillRect(hdc, &rc, hbr_black);
6822
6823     SelectObject(hdc, hbm_im);
6824     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6825     SelectObject(hdc, hbm_orig);
6826     ImageList_Add(himl, hbm_im, hbm_mask); 
6827
6828     SelectObject(hdc, hbm_im);
6829     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6830     SelectObject(hdc, hbm_orig);
6831     ImageList_Add(himl, hbm_im, hbm_mask);
6832
6833     DeleteObject(hbm_mask);
6834     DeleteObject(hbm_im);
6835     DeleteDC(hdc);
6836
6837     return himl;
6838 }
6839
6840 /***
6841  * DESCRIPTION:
6842  * Sets the extended listview style.
6843  *
6844  * PARAMETERS:
6845  * [I] infoPtr : valid pointer to the listview structure
6846  * [I] dwMask : mask
6847  * [I] dwStyle : style
6848  *
6849  * RETURN:
6850  *   SUCCESS : previous style
6851  *   FAILURE : 0
6852  */
6853 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6854 {
6855     DWORD dwOldStyle = infoPtr->dwLvExStyle;
6856
6857     /* set new style */
6858     if (dwMask)
6859         infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6860     else
6861         infoPtr->dwLvExStyle = dwStyle;
6862
6863     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6864     {
6865         HIMAGELIST himl = 0;
6866         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6867             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6868         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6869     }
6870
6871     return dwOldStyle;
6872 }
6873
6874 /***
6875  * DESCRIPTION:
6876  * Sets the new hot cursor used during hot tracking and hover selection.
6877  *
6878  * PARAMETER(S):
6879  * [I] infoPtr : valid pointer to the listview structure
6880  * [I] hCursor : the new hot cursor handle
6881  *
6882  * RETURN:
6883  * Returns the previous hot cursor
6884  */
6885 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6886 {
6887     HCURSOR oldCursor = infoPtr->hHotCursor;
6888     
6889     infoPtr->hHotCursor = hCursor;
6890
6891     return oldCursor;
6892 }
6893
6894
6895 /***
6896  * DESCRIPTION:
6897  * Sets the hot item index.
6898  *
6899  * PARAMETERS:
6900  * [I] infoPtr : valid pointer to the listview structure
6901  * [I] iIndex : index
6902  *
6903  * RETURN:
6904  *   SUCCESS : previous hot item index
6905  *   FAILURE : -1 (no hot item)
6906  */
6907 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6908 {
6909     INT iOldIndex = infoPtr->nHotItem;
6910     
6911     infoPtr->nHotItem = iIndex;
6912     
6913     return iOldIndex;
6914 }
6915
6916
6917 /***
6918  * DESCRIPTION:
6919  * Sets the amount of time the cursor must hover over an item before it is selected.
6920  *
6921  * PARAMETER(S):
6922  * [I] infoPtr : valid pointer to the listview structure
6923  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6924  *
6925  * RETURN:
6926  * Returns the previous hover time
6927  */
6928 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6929 {
6930     DWORD oldHoverTime = infoPtr->dwHoverTime;
6931     
6932     infoPtr->dwHoverTime = dwHoverTime;
6933     
6934     return oldHoverTime;
6935 }
6936
6937 /***
6938  * DESCRIPTION:
6939  * Sets spacing for icons of LVS_ICON style.
6940  *
6941  * PARAMETER(S):
6942  * [I] infoPtr : valid pointer to the listview structure
6943  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6944  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6945  *
6946  * RETURN:
6947  *   MAKELONG(oldcx, oldcy)
6948  */
6949 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6950 {
6951     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6952     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6953
6954     TRACE("requested=(%d,%d)\n", cx, cy);
6955     
6956     /* this is supported only for LVS_ICON style */
6957     if (uView != LVS_ICON) return oldspacing;
6958   
6959     /* set to defaults, if instructed to */
6960     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6961     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6962
6963     /* if 0 then compute width
6964      * FIXME: Should scan each item and determine max width of
6965      *        icon or label, then make that the width */
6966     if (cx == 0)
6967         cx = infoPtr->iconSpacing.cx;
6968
6969     /* if 0 then compute height */
6970     if (cy == 0) 
6971         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6972              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6973     
6974
6975     infoPtr->iconSpacing.cx = cx;
6976     infoPtr->iconSpacing.cy = cy;
6977
6978     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6979           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
6980           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6981           infoPtr->ntmHeight);
6982
6983     /* these depend on the iconSpacing */
6984     LISTVIEW_UpdateItemSize(infoPtr);
6985
6986     return oldspacing;
6987 }
6988
6989 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6990 {
6991     INT cx, cy;
6992     
6993     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6994     {
6995         size->cx = cx;
6996         size->cy = cy;
6997     }
6998     else
6999     {
7000         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7001         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7002     }
7003 }
7004
7005 /***
7006  * DESCRIPTION:
7007  * Sets image lists.
7008  *
7009  * PARAMETER(S):
7010  * [I] infoPtr : valid pointer to the listview structure
7011  * [I] nType : image list type
7012  * [I] himl : image list handle
7013  *
7014  * RETURN:
7015  *   SUCCESS : old image list
7016  *   FAILURE : NULL
7017  */
7018 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7019 {
7020     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7021     INT oldHeight = infoPtr->nItemHeight;
7022     HIMAGELIST himlOld = 0;
7023
7024     TRACE("(nType=%d, himl=%p\n", nType, himl);
7025
7026     switch (nType)
7027     {
7028     case LVSIL_NORMAL:
7029         himlOld = infoPtr->himlNormal;
7030         infoPtr->himlNormal = himl;
7031         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7032         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7033     break;
7034
7035     case LVSIL_SMALL:
7036         himlOld = infoPtr->himlSmall;
7037         infoPtr->himlSmall = himl;
7038          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7039     break;
7040
7041     case LVSIL_STATE:
7042         himlOld = infoPtr->himlState;
7043         infoPtr->himlState = himl;
7044         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7045         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7046     break;
7047
7048     default:
7049         ERR("Unknown icon type=%d\n", nType);
7050         return NULL;
7051     }
7052
7053     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7054     if (infoPtr->nItemHeight != oldHeight)
7055         LISTVIEW_UpdateScroll(infoPtr);
7056
7057     return himlOld;
7058 }
7059
7060 /***
7061  * DESCRIPTION:
7062  * Preallocates memory (does *not* set the actual count of items !)
7063  *
7064  * PARAMETER(S):
7065  * [I] infoPtr : valid pointer to the listview structure
7066  * [I] nItems : item count (projected number of items to allocate)
7067  * [I] dwFlags : update flags
7068  *
7069  * RETURN:
7070  *   SUCCESS : TRUE
7071  *   FAILURE : FALSE
7072  */
7073 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7074 {
7075     TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7076
7077     if (infoPtr->dwStyle & LVS_OWNERDATA)
7078     {
7079         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7080         INT nOldCount = infoPtr->nItemCount;
7081
7082         if (nItems < nOldCount)
7083         {
7084             RANGE range = { nItems, nOldCount };
7085             ranges_del(infoPtr->selectionRanges, range);
7086             if (infoPtr->nFocusedItem >= nItems)
7087             {
7088                 infoPtr->nFocusedItem = -1;
7089                 SetRectEmpty(&infoPtr->rcFocus);
7090             }
7091         }
7092
7093         infoPtr->nItemCount = nItems;
7094         LISTVIEW_UpdateScroll(infoPtr);
7095
7096         /* the flags are valid only in ownerdata report and list modes */
7097         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7098
7099         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7100             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7101
7102         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7103             LISTVIEW_InvalidateList(infoPtr);
7104         else
7105         {
7106             INT nFrom, nTo;
7107             POINT Origin;
7108             RECT rcErase;
7109             
7110             LISTVIEW_GetOrigin(infoPtr, &Origin);
7111             nFrom = min(nOldCount, nItems);
7112             nTo = max(nOldCount, nItems);
7113     
7114             if (uView == LVS_REPORT)
7115             {
7116                 rcErase.left = 0;
7117                 rcErase.top = nFrom * infoPtr->nItemHeight;
7118                 rcErase.right = infoPtr->nItemWidth;
7119                 rcErase.bottom = nTo * infoPtr->nItemHeight;
7120                 OffsetRect(&rcErase, Origin.x, Origin.y);
7121                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7122                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7123             }
7124             else /* LVS_LIST */
7125             {
7126                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7127
7128                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7129                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7130                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7131                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7132                 OffsetRect(&rcErase, Origin.x, Origin.y);
7133                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7134                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7135
7136                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7137                 rcErase.top = 0;
7138                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7139                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7140                 OffsetRect(&rcErase, Origin.x, Origin.y);
7141                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7142                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7143             }
7144         }
7145     }
7146     else
7147     {
7148         /* According to MSDN for non-LVS_OWNERDATA this is just
7149          * a performance issue. The control allocates its internal
7150          * data structures for the number of items specified. It
7151          * cuts down on the number of memory allocations. Therefore
7152          * we will just issue a WARN here
7153          */
7154         WARN("for non-ownerdata performance option not implemented.\n");
7155     }
7156
7157     return TRUE;
7158 }
7159
7160 /***
7161  * DESCRIPTION:
7162  * Sets the position of an item.
7163  *
7164  * PARAMETER(S):
7165  * [I] infoPtr : valid pointer to the listview structure
7166  * [I] nItem : item index
7167  * [I] pt : coordinate
7168  *
7169  * RETURN:
7170  *   SUCCESS : TRUE
7171  *   FAILURE : FALSE
7172  */
7173 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7174 {
7175     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7176     POINT Origin;
7177
7178     TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7179
7180     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7181         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7182
7183     LISTVIEW_GetOrigin(infoPtr, &Origin);
7184
7185     /* This point value seems to be an undocumented feature.
7186      * The best guess is that it means either at the origin, 
7187      * or at true beginning of the list. I will assume the origin. */
7188     if ((pt.x == -1) && (pt.y == -1))
7189         pt = Origin;
7190     
7191     if (uView == LVS_ICON)
7192     {
7193         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7194         pt.y -= ICON_TOP_PADDING;
7195     }
7196     pt.x -= Origin.x;
7197     pt.y -= Origin.y;
7198
7199     infoPtr->bAutoarrange = FALSE;
7200
7201     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7202 }
7203
7204 /***
7205  * DESCRIPTION:
7206  * Sets the state of one or many items.
7207  *
7208  * PARAMETER(S):
7209  * [I] infoPtr : valid pointer to the listview structure
7210  * [I] nItem : item index
7211  * [I] lpLVItem : item or subitem info
7212  *
7213  * RETURN:
7214  *   SUCCESS : TRUE
7215  *   FAILURE : FALSE
7216  */
7217 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7218 {
7219     BOOL bResult = TRUE;
7220     LVITEMW lvItem;
7221
7222     lvItem.iItem = nItem;
7223     lvItem.iSubItem = 0;
7224     lvItem.mask = LVIF_STATE;
7225     lvItem.state = lpLVItem->state;
7226     lvItem.stateMask = lpLVItem->stateMask;
7227     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7228
7229     if (nItem == -1)
7230     {
7231         /* apply to all items */
7232         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7233             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7234     }
7235     else
7236         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7237
7238     /*
7239      * Update selection mark
7240      *
7241      * Investigation on windows 2k showed that selection mark was updated
7242      * whenever a new selection was made, but if the selected item was
7243      * unselected it was not updated.
7244      *
7245      * we are probably still not 100% accurate, but this at least sets the
7246      * proper selection mark when it is needed
7247      */
7248
7249     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7250         ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7251     {
7252         int i;
7253         infoPtr->nSelectionMark = -1;
7254         for (i = 0; i < infoPtr->nItemCount; i++)
7255         {
7256             if (infoPtr->uCallbackMask & LVIS_SELECTED)
7257             {
7258                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7259                 {
7260                     infoPtr->nSelectionMark = i;
7261                     break;
7262                 }
7263             }
7264             else if (ranges_contain(infoPtr->selectionRanges, i))
7265             {
7266                 infoPtr->nSelectionMark = i;
7267                 break;
7268             }
7269         }
7270     }
7271
7272     return bResult;
7273 }
7274
7275 /***
7276  * DESCRIPTION:
7277  * Sets the text of an item or subitem.
7278  *
7279  * PARAMETER(S):
7280  * [I] hwnd : window handle
7281  * [I] nItem : item index
7282  * [I] lpLVItem : item or subitem info
7283  * [I] isW : TRUE if input is Unicode
7284  *
7285  * RETURN:
7286  *   SUCCESS : TRUE
7287  *   FAILURE : FALSE
7288  */
7289 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7290 {
7291     LVITEMW lvItem;
7292
7293     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7294     
7295     lvItem.iItem = nItem;
7296     lvItem.iSubItem = lpLVItem->iSubItem;
7297     lvItem.mask = LVIF_TEXT;
7298     lvItem.pszText = lpLVItem->pszText;
7299     lvItem.cchTextMax = lpLVItem->cchTextMax;
7300     
7301     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7302
7303     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
7304 }
7305
7306 /***
7307  * DESCRIPTION:
7308  * Set item index that marks the start of a multiple selection.
7309  *
7310  * PARAMETER(S):
7311  * [I] infoPtr : valid pointer to the listview structure
7312  * [I] nIndex : index
7313  *
7314  * RETURN:
7315  * Index number or -1 if there is no selection mark.
7316  */
7317 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7318 {
7319   INT nOldIndex = infoPtr->nSelectionMark;
7320
7321   TRACE("(nIndex=%d)\n", nIndex);
7322
7323   infoPtr->nSelectionMark = nIndex;
7324
7325   return nOldIndex;
7326 }
7327
7328 /***
7329  * DESCRIPTION:
7330  * Sets the text background color.
7331  *
7332  * PARAMETER(S):
7333  * [I] infoPtr : valid pointer to the listview structure
7334  * [I] clrTextBk : text background color
7335  *
7336  * RETURN:
7337  *   SUCCESS : TRUE
7338  *   FAILURE : FALSE
7339  */
7340 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7341 {
7342     TRACE("(clrTextBk=%lx)\n", clrTextBk);
7343
7344     if (infoPtr->clrTextBk != clrTextBk)
7345     {
7346         infoPtr->clrTextBk = clrTextBk;
7347         LISTVIEW_InvalidateList(infoPtr);
7348     }
7349     
7350   return TRUE;
7351 }
7352
7353 /***
7354  * DESCRIPTION:
7355  * Sets the text foreground color.
7356  *
7357  * PARAMETER(S):
7358  * [I] infoPtr : valid pointer to the listview structure
7359  * [I] clrText : text color
7360  *
7361  * RETURN:
7362  *   SUCCESS : TRUE
7363  *   FAILURE : FALSE
7364  */
7365 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7366 {
7367     TRACE("(clrText=%lx)\n", clrText);
7368
7369     if (infoPtr->clrText != clrText)
7370     {
7371         infoPtr->clrText = clrText;
7372         LISTVIEW_InvalidateList(infoPtr);
7373     }
7374
7375     return TRUE;
7376 }
7377
7378 /***
7379  * DESCRIPTION:
7380  * Determines which listview item is located at the specified position.
7381  *
7382  * PARAMETER(S):
7383  * [I] infoPtr        : valid pointer to the listview structure
7384  * [I] hwndNewToolTip : handle to new ToolTip
7385  *
7386  * RETURN:
7387  *   old tool tip
7388  */
7389 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7390 {
7391   HWND hwndOldToolTip = infoPtr->hwndToolTip;
7392   infoPtr->hwndToolTip = hwndNewToolTip;
7393   return hwndOldToolTip;
7394 }
7395
7396 /* LISTVIEW_SetUnicodeFormat */
7397 /* LISTVIEW_SetWorkAreas */
7398
7399 /***
7400  * DESCRIPTION:
7401  * Callback internally used by LISTVIEW_SortItems()
7402  *
7403  * PARAMETER(S):
7404  * [I] first : pointer to first ITEM_INFO to compare
7405  * [I] second : pointer to second ITEM_INFO to compare
7406  * [I] lParam : HWND of control
7407  *
7408  * RETURN:
7409  *   if first comes before second : negative
7410  *   if first comes after second : positive
7411  *   if first and second are equivalent : zero
7412  */
7413 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7414 {
7415   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7416   ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7417   ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7418
7419   /* Forward the call to the client defined callback */
7420   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7421 }
7422
7423 /***
7424  * DESCRIPTION:
7425  * Sorts the listview items.
7426  *
7427  * PARAMETER(S):
7428  * [I] infoPtr : valid pointer to the listview structure
7429  * [I] pfnCompare : application-defined value
7430  * [I] lParamSort : pointer to comparision callback
7431  *
7432  * RETURN:
7433  *   SUCCESS : TRUE
7434  *   FAILURE : FALSE
7435  */
7436 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7437 {
7438     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7439     HDPA hdpaSubItems;
7440     ITEM_INFO *lpItem;
7441     LPVOID selectionMarkItem;
7442     LVITEMW item;
7443     int i;
7444
7445     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7446
7447     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7448
7449     if (!pfnCompare) return FALSE;
7450     if (!infoPtr->hdpaItems) return FALSE;
7451
7452     /* if there are 0 or 1 items, there is no need to sort */
7453     if (infoPtr->nItemCount < 2) return TRUE;
7454
7455     if (infoPtr->nFocusedItem >= 0)
7456     {
7457         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7458         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7459         if (lpItem) lpItem->state |= LVIS_FOCUSED;
7460     }
7461     /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7462     /*        clear the lpItem->state for non-selected ones */
7463     /*        remove the selection ranges */
7464     
7465     infoPtr->pfnCompare = pfnCompare;
7466     infoPtr->lParamSort = lParamSort;
7467     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7468
7469     /* Adjust selections and indices so that they are the way they should
7470      * be after the sort (otherwise, the list items move around, but
7471      * whatever is at the item's previous original position will be
7472      * selected instead)
7473      */
7474     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7475     for (i=0; i < infoPtr->nItemCount; i++)
7476     {
7477         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7478         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7479
7480         if (lpItem->state & LVIS_SELECTED)
7481         {
7482             item.state = LVIS_SELECTED;
7483             item.stateMask = LVIS_SELECTED;
7484             LISTVIEW_SetItemState(infoPtr, i, &item);
7485         }
7486         if (lpItem->state & LVIS_FOCUSED)
7487         {
7488             infoPtr->nFocusedItem = i;
7489             lpItem->state &= ~LVIS_FOCUSED;
7490         }
7491     }
7492     if (selectionMarkItem != NULL)
7493         infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7494     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7495
7496     /* refresh the display */
7497     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7498         LISTVIEW_InvalidateList(infoPtr);
7499
7500     return TRUE;
7501 }
7502
7503 /***
7504  * DESCRIPTION:
7505  * Update theme handle after a theme change.
7506  *
7507  * PARAMETER(S):
7508  * [I] infoPtr : valid pointer to the listview structure
7509  *
7510  * RETURN:
7511  *   SUCCESS : 0
7512  *   FAILURE : something else
7513  */
7514 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7515 {
7516     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7517     CloseThemeData(theme);
7518     OpenThemeData(infoPtr->hwndSelf, themeClass);
7519     return 0;
7520 }
7521
7522 /***
7523  * DESCRIPTION:
7524  * Updates an items or rearranges the listview control.
7525  *
7526  * PARAMETER(S):
7527  * [I] infoPtr : valid pointer to the listview structure
7528  * [I] nItem : item index
7529  *
7530  * RETURN:
7531  *   SUCCESS : TRUE
7532  *   FAILURE : FALSE
7533  */
7534 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7535 {
7536     TRACE("(nItem=%d)\n", nItem);
7537
7538     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7539
7540     /* rearrange with default alignment style */
7541     if (is_autoarrange(infoPtr))
7542         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7543     else
7544         LISTVIEW_InvalidateItem(infoPtr, nItem);
7545
7546     return TRUE;
7547 }
7548
7549         
7550 /***
7551  * DESCRIPTION:
7552  * Creates the listview control.
7553  *
7554  * PARAMETER(S):
7555  * [I] hwnd : window handle
7556  * [I] lpcs : the create parameters
7557  *
7558  * RETURN:
7559  *   Success: 0
7560  *   Failure: -1
7561  */
7562 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7563 {
7564   LISTVIEW_INFO *infoPtr;
7565   UINT uView = lpcs->style & LVS_TYPEMASK;
7566   LOGFONTW logFont;
7567
7568   TRACE("(lpcs=%p)\n", lpcs);
7569
7570   /* initialize info pointer */
7571   infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7572   if (!infoPtr) return -1;
7573
7574   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7575
7576   infoPtr->hwndSelf = hwnd;
7577   infoPtr->dwStyle = lpcs->style;
7578   /* determine the type of structures to use */
7579   infoPtr->hwndNotify = lpcs->hwndParent;
7580   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7581                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7582
7583   /* initialize color information  */
7584   infoPtr->clrBk = CLR_NONE;
7585   infoPtr->clrText = comctl32_color.clrWindowText;
7586   infoPtr->clrTextBk = CLR_DEFAULT;
7587   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7588
7589   /* set default values */
7590   infoPtr->nFocusedItem = -1;
7591   infoPtr->nSelectionMark = -1;
7592   infoPtr->nHotItem = -1;
7593   infoPtr->bRedraw = TRUE;
7594   infoPtr->bNoItemMetrics = TRUE;
7595   infoPtr->bDoChangeNotify = TRUE;
7596   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7597   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7598   infoPtr->nEditLabelItem = -1;
7599   infoPtr->dwHoverTime = -1; /* default system hover time */
7600   infoPtr->nMeasureItemHeight = 0;
7601
7602   /* get default font (icon title) */
7603   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7604   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7605   infoPtr->hFont = infoPtr->hDefaultFont;
7606   LISTVIEW_SaveTextMetrics(infoPtr);
7607
7608   /* create header */
7609   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7610     WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7611     0, 0, 0, 0, hwnd, NULL,
7612     lpcs->hInstance, NULL);
7613   if (!infoPtr->hwndHeader) goto fail;
7614
7615   /* set header unicode format */
7616   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7617
7618   /* set header font */
7619   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7620
7621   /* allocate memory for the data structure */
7622   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7623   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7624   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
7625   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
7626   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7627
7628   /* initialize the icon sizes */
7629   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7630   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7631
7632   /* init item size to avoid division by 0 */
7633   LISTVIEW_UpdateItemSize (infoPtr);
7634   
7635   if (uView == LVS_REPORT)
7636   {
7637     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7638     {
7639       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7640     }
7641     else
7642     {
7643       /* set HDS_HIDDEN flag to hide the header bar */
7644       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7645                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7646     }
7647   }
7648
7649   OpenThemeData(hwnd, themeClass);
7650
7651   return 0;
7652
7653 fail:
7654     DestroyWindow(infoPtr->hwndHeader);
7655     ranges_destroy(infoPtr->selectionRanges);
7656     DPA_Destroy(infoPtr->hdpaItems);
7657     DPA_Destroy(infoPtr->hdpaPosX);
7658     DPA_Destroy(infoPtr->hdpaPosY);
7659     DPA_Destroy(infoPtr->hdpaColumns);
7660     Free(infoPtr);
7661     return -1;
7662 }
7663
7664 /***
7665  * DESCRIPTION:
7666  * Destroys the listview control.
7667  *
7668  * PARAMETER(S):
7669  * [I] infoPtr : valid pointer to the listview structure
7670  *
7671  * RETURN:
7672  *   Success: 0
7673  *   Failure: -1
7674  */
7675 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7676 {
7677     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7678     CloseThemeData(theme);
7679     return 0;
7680 }
7681
7682 /***
7683  * DESCRIPTION:
7684  * Enables the listview control.
7685  *
7686  * PARAMETER(S):
7687  * [I] infoPtr : valid pointer to the listview structure
7688  * [I] bEnable : specifies whether to enable or disable the window
7689  *
7690  * RETURN:
7691  *   SUCCESS : TRUE
7692  *   FAILURE : FALSE
7693  */
7694 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7695 {
7696     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7697         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7698     return TRUE;
7699 }
7700
7701 /***
7702  * DESCRIPTION:
7703  * Erases the background of the listview control.
7704  *
7705  * PARAMETER(S):
7706  * [I] infoPtr : valid pointer to the listview structure
7707  * [I] hdc : device context handle
7708  *
7709  * RETURN:
7710  *   SUCCESS : TRUE
7711  *   FAILURE : FALSE
7712  */
7713 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7714 {
7715     RECT rc;
7716
7717     TRACE("(hdc=%p)\n", hdc);
7718
7719     if (!GetClipBox(hdc, &rc)) return FALSE;
7720
7721     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7722 }
7723         
7724
7725 /***
7726  * DESCRIPTION:
7727  * Helper function for LISTVIEW_[HV]Scroll *only*.
7728  * Performs vertical/horizontal scrolling by a give amount.
7729  *
7730  * PARAMETER(S):
7731  * [I] infoPtr : valid pointer to the listview structure
7732  * [I] dx : amount of horizontal scroll
7733  * [I] dy : amount of vertical scroll
7734  */
7735 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7736 {
7737     /* now we can scroll the list */
7738     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
7739                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7740     /* if we have focus, adjust rect */
7741     OffsetRect(&infoPtr->rcFocus, dx, dy);
7742     UpdateWindow(infoPtr->hwndSelf);
7743 }
7744
7745 /***
7746  * DESCRIPTION:
7747  * Performs vertical scrolling.
7748  *
7749  * PARAMETER(S):
7750  * [I] infoPtr : valid pointer to the listview structure
7751  * [I] nScrollCode : scroll code
7752  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7753  * [I] hScrollWnd  : scrollbar control window handle
7754  *
7755  * RETURN:
7756  * Zero
7757  *
7758  * NOTES:
7759  *   SB_LINEUP/SB_LINEDOWN:
7760  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
7761  *        for LVS_REPORT is 1 line
7762  *        for LVS_LIST cannot occur
7763  *
7764  */
7765 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
7766                                 INT nScrollDiff, HWND hScrollWnd)
7767 {
7768     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7769     INT nOldScrollPos, nNewScrollPos;
7770     SCROLLINFO scrollInfo;
7771     BOOL is_an_icon;
7772
7773     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7774         debugscrollcode(nScrollCode), nScrollDiff);
7775
7776     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7777
7778     scrollInfo.cbSize = sizeof(SCROLLINFO);
7779     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7780
7781     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7782
7783     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7784
7785     nOldScrollPos = scrollInfo.nPos;
7786     switch (nScrollCode)
7787     {
7788     case SB_INTERNAL:
7789         break;
7790
7791     case SB_LINEUP:
7792         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7793         break;
7794
7795     case SB_LINEDOWN:
7796         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7797         break;
7798
7799     case SB_PAGEUP:
7800         nScrollDiff = -scrollInfo.nPage;
7801         break;
7802
7803     case SB_PAGEDOWN:
7804         nScrollDiff = scrollInfo.nPage;
7805         break;
7806
7807     case SB_THUMBPOSITION:
7808     case SB_THUMBTRACK:
7809         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7810         break;
7811
7812     default:
7813         nScrollDiff = 0;
7814     }
7815
7816     /* quit right away if pos isn't changing */
7817     if (nScrollDiff == 0) return 0;
7818     
7819     /* calculate new position, and handle overflows */
7820     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7821     if (nScrollDiff > 0) {
7822         if (nNewScrollPos < nOldScrollPos ||
7823             nNewScrollPos > scrollInfo.nMax)
7824             nNewScrollPos = scrollInfo.nMax;
7825     } else {
7826         if (nNewScrollPos > nOldScrollPos ||
7827             nNewScrollPos < scrollInfo.nMin)
7828             nNewScrollPos = scrollInfo.nMin;
7829     }
7830
7831     /* set the new position, and reread in case it changed */
7832     scrollInfo.fMask = SIF_POS;
7833     scrollInfo.nPos = nNewScrollPos;
7834     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7835     
7836     /* carry on only if it really changed */
7837     if (nNewScrollPos == nOldScrollPos) return 0;
7838     
7839     /* now adjust to client coordinates */
7840     nScrollDiff = nOldScrollPos - nNewScrollPos;
7841     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7842    
7843     /* and scroll the window */ 
7844     scroll_list(infoPtr, 0, nScrollDiff);
7845
7846     return 0;
7847 }
7848
7849 /***
7850  * DESCRIPTION:
7851  * Performs horizontal scrolling.
7852  *
7853  * PARAMETER(S):
7854  * [I] infoPtr : valid pointer to the listview structure
7855  * [I] nScrollCode : scroll code
7856  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7857  * [I] hScrollWnd  : scrollbar control window handle
7858  *
7859  * RETURN:
7860  * Zero
7861  *
7862  * NOTES:
7863  *   SB_LINELEFT/SB_LINERIGHT:
7864  *        for LVS_ICON, LVS_SMALLICON  1 pixel
7865  *        for LVS_REPORT is 1 pixel
7866  *        for LVS_LIST  is 1 column --> which is a 1 because the
7867  *                                      scroll is based on columns not pixels
7868  *
7869  */
7870 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7871                                 INT nScrollDiff, HWND hScrollWnd)
7872 {
7873     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7874     INT nOldScrollPos, nNewScrollPos;
7875     SCROLLINFO scrollInfo;
7876
7877     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7878         debugscrollcode(nScrollCode), nScrollDiff);
7879
7880     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7881
7882     scrollInfo.cbSize = sizeof(SCROLLINFO);
7883     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7884
7885     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7886
7887     nOldScrollPos = scrollInfo.nPos;
7888
7889     switch (nScrollCode)
7890     {
7891     case SB_INTERNAL:
7892         break;
7893
7894     case SB_LINELEFT:
7895         nScrollDiff = -1;
7896         break;
7897
7898     case SB_LINERIGHT:
7899         nScrollDiff = 1;
7900         break;
7901
7902     case SB_PAGELEFT:
7903         nScrollDiff = -scrollInfo.nPage;
7904         break;
7905
7906     case SB_PAGERIGHT:
7907         nScrollDiff = scrollInfo.nPage;
7908         break;
7909
7910     case SB_THUMBPOSITION:
7911     case SB_THUMBTRACK:
7912         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7913         break;
7914
7915     default:
7916         nScrollDiff = 0;
7917     }
7918
7919     /* quit right away if pos isn't changing */
7920     if (nScrollDiff == 0) return 0;
7921     
7922     /* calculate new position, and handle overflows */
7923     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7924     if (nScrollDiff > 0) {
7925         if (nNewScrollPos < nOldScrollPos ||
7926             nNewScrollPos > scrollInfo.nMax)
7927             nNewScrollPos = scrollInfo.nMax;
7928     } else {
7929         if (nNewScrollPos > nOldScrollPos ||
7930             nNewScrollPos < scrollInfo.nMin)
7931             nNewScrollPos = scrollInfo.nMin;
7932     }
7933
7934     /* set the new position, and reread in case it changed */
7935     scrollInfo.fMask = SIF_POS;
7936     scrollInfo.nPos = nNewScrollPos;
7937     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7938     
7939     /* carry on only if it really changed */
7940     if (nNewScrollPos == nOldScrollPos) return 0;
7941     
7942     if(uView == LVS_REPORT)
7943         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7944       
7945     /* now adjust to client coordinates */
7946     nScrollDiff = nOldScrollPos - nNewScrollPos;
7947     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7948    
7949     /* and scroll the window */
7950     scroll_list(infoPtr, nScrollDiff, 0);
7951
7952   return 0;
7953 }
7954
7955 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7956 {
7957     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7958     INT gcWheelDelta = 0;
7959     INT pulScrollLines = 3;
7960     SCROLLINFO scrollInfo;
7961
7962     TRACE("(wheelDelta=%d)\n", wheelDelta);
7963
7964     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7965     gcWheelDelta -= wheelDelta;
7966
7967     scrollInfo.cbSize = sizeof(SCROLLINFO);
7968     scrollInfo.fMask = SIF_POS;
7969
7970     switch(uView)
7971     {
7972     case LVS_ICON:
7973     case LVS_SMALLICON:
7974        /*
7975         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7976         *  should be fixed in the future.
7977         */
7978         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7979                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7980         break;
7981
7982     case LVS_REPORT:
7983         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7984         {
7985             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7986             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7987             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7988         }
7989         break;
7990
7991     case LVS_LIST:
7992         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7993         break;
7994     }
7995     return 0;
7996 }
7997
7998 /***
7999  * DESCRIPTION:
8000  * ???
8001  *
8002  * PARAMETER(S):
8003  * [I] infoPtr : valid pointer to the listview structure
8004  * [I] nVirtualKey : virtual key
8005  * [I] lKeyData : key data
8006  *
8007  * RETURN:
8008  * Zero
8009  */
8010 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8011 {
8012   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8013   HWND hwndSelf = infoPtr->hwndSelf;
8014   INT nItem = -1;
8015   NMLVKEYDOWN nmKeyDown;
8016
8017   TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8018
8019   /* send LVN_KEYDOWN notification */
8020   nmKeyDown.wVKey = nVirtualKey;
8021   nmKeyDown.flags = 0;
8022   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8023   if (!IsWindow(hwndSelf))
8024     return 0;
8025
8026   switch (nVirtualKey)
8027   {
8028   case VK_SPACE:
8029     nItem = infoPtr->nFocusedItem;
8030     break;
8031
8032   case VK_RETURN:
8033     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8034     {
8035         if (!notify(infoPtr, NM_RETURN)) return 0;
8036         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8037     }
8038     break;
8039
8040   case VK_HOME:
8041     if (infoPtr->nItemCount > 0)
8042       nItem = 0;
8043     break;
8044
8045   case VK_END:
8046     if (infoPtr->nItemCount > 0)
8047       nItem = infoPtr->nItemCount - 1;
8048     break;
8049
8050   case VK_LEFT:
8051     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8052     break;
8053
8054   case VK_UP:
8055     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8056     break;
8057
8058   case VK_RIGHT:
8059     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8060     break;
8061
8062   case VK_DOWN:
8063     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8064     break;
8065
8066   case VK_PRIOR:
8067     if (uView == LVS_REPORT)
8068     {
8069       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8070       if (infoPtr->nFocusedItem == topidx)
8071         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8072       else
8073         nItem = topidx;
8074     }
8075     else
8076       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8077                                     * LISTVIEW_GetCountPerRow(infoPtr);
8078     if(nItem < 0) nItem = 0;
8079     break;
8080
8081   case VK_NEXT:
8082     if (uView == LVS_REPORT)
8083     {
8084       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8085       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8086       if (infoPtr->nFocusedItem == topidx + cnt - 1)
8087         nItem = infoPtr->nFocusedItem + cnt - 1;
8088       else
8089         nItem = topidx + cnt - 1;
8090     }
8091     else
8092       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8093                                     * LISTVIEW_GetCountPerRow(infoPtr);
8094     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8095     break;
8096   }
8097
8098   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8099       LISTVIEW_KeySelection(infoPtr, nItem);
8100
8101   return 0;
8102 }
8103
8104 /***
8105  * DESCRIPTION:
8106  * Kills the focus.
8107  *
8108  * PARAMETER(S):
8109  * [I] infoPtr : valid pointer to the listview structure
8110  *
8111  * RETURN:
8112  * Zero
8113  */
8114 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8115 {
8116     TRACE("()\n");
8117
8118     /* if we did not have the focus, there's nothing to do */
8119     if (!infoPtr->bFocus) return 0;
8120    
8121     /* send NM_KILLFOCUS notification */
8122     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8123
8124     /* if we have a focus rectagle, get rid of it */
8125     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8126     
8127     /* set window focus flag */
8128     infoPtr->bFocus = FALSE;
8129
8130     /* invalidate the selected items before reseting focus flag */
8131     LISTVIEW_InvalidateSelectedItems(infoPtr);
8132     
8133     return 0;
8134 }
8135
8136 /***
8137  * DESCRIPTION:
8138  * Processes double click messages (left mouse button).
8139  *
8140  * PARAMETER(S):
8141  * [I] infoPtr : valid pointer to the listview structure
8142  * [I] wKey : key flag
8143  * [I] x,y : mouse coordinate
8144  *
8145  * RETURN:
8146  * Zero
8147  */
8148 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8149 {
8150     LVHITTESTINFO htInfo;
8151
8152     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8153
8154     /* send NM_RELEASEDCAPTURE notification */
8155     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8156
8157     htInfo.pt.x = x;
8158     htInfo.pt.y = y;
8159
8160     /* send NM_DBLCLK notification */
8161     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8162     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8163
8164     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8165     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8166
8167     return 0;
8168 }
8169
8170 /***
8171  * DESCRIPTION:
8172  * Processes mouse down messages (left mouse button).
8173  *
8174  * PARAMETERS:
8175  *   infoPtr  [I ] valid pointer to the listview structure
8176  *   wKey     [I ] key flag
8177  *   x,y      [I ] mouse coordinate
8178  *
8179  * RETURN:
8180  *   Zero
8181  */
8182 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8183 {
8184   LVHITTESTINFO lvHitTestInfo;
8185   static BOOL bGroupSelect = TRUE;
8186   POINT pt = { x, y };
8187   INT nItem;
8188
8189   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8190
8191   /* send NM_RELEASEDCAPTURE notification */
8192   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8193
8194   if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8195
8196   /* set left button down flag and record the click position */
8197   infoPtr->bLButtonDown = TRUE;
8198   infoPtr->ptClickPos = pt;
8199
8200   lvHitTestInfo.pt.x = x;
8201   lvHitTestInfo.pt.y = y;
8202
8203   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8204   TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8205   infoPtr->nEditLabelItem = -1;
8206   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8207   {
8208     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8209     {
8210         DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8211         if(state == 1 || state == 2)
8212         {
8213             LVITEMW lvitem;
8214             state ^= 3;
8215             lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8216             lvitem.stateMask = LVIS_STATEIMAGEMASK;
8217             LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8218         }
8219         return 0;
8220     }
8221
8222     if (infoPtr->dwStyle & LVS_SINGLESEL)
8223     {
8224       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8225         infoPtr->nEditLabelItem = nItem;
8226       else
8227         LISTVIEW_SetSelection(infoPtr, nItem);
8228     }
8229     else
8230     {
8231       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8232       {
8233         if (bGroupSelect)
8234         {
8235           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8236           LISTVIEW_SetItemFocus(infoPtr, nItem);
8237           infoPtr->nSelectionMark = nItem;
8238         }
8239         else
8240         {
8241           LVITEMW item;
8242
8243           item.state = LVIS_SELECTED | LVIS_FOCUSED;
8244           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8245
8246           LISTVIEW_SetItemState(infoPtr,nItem,&item);
8247           infoPtr->nSelectionMark = nItem;
8248         }
8249       }
8250       else if (wKey & MK_CONTROL)
8251       {
8252         LVITEMW item;
8253
8254         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8255         
8256         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8257         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8258         LISTVIEW_SetItemState(infoPtr, nItem, &item);
8259         infoPtr->nSelectionMark = nItem;
8260       }
8261       else  if (wKey & MK_SHIFT)
8262       {
8263         LISTVIEW_SetGroupSelection(infoPtr, nItem);
8264       }
8265       else
8266       {
8267         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8268           infoPtr->nEditLabelItem = nItem;
8269
8270         /* set selection (clears other pre-existing selections) */
8271         LISTVIEW_SetSelection(infoPtr, nItem);
8272       }
8273     }
8274   }
8275   else
8276   {
8277     /* remove all selections */
8278     LISTVIEW_DeselectAll(infoPtr);
8279     ReleaseCapture();
8280   }
8281
8282   return 0;
8283 }
8284
8285 /***
8286  * DESCRIPTION:
8287  * Processes mouse up messages (left mouse button).
8288  *
8289  * PARAMETERS:
8290  *   infoPtr [I ] valid pointer to the listview structure
8291  *   wKey    [I ] key flag
8292  *   x,y     [I ] mouse coordinate
8293  *
8294  * RETURN:
8295  *   Zero
8296  */
8297 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8298 {
8299     LVHITTESTINFO lvHitTestInfo;
8300     
8301     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8302
8303     if (!infoPtr->bLButtonDown) return 0;
8304
8305     lvHitTestInfo.pt.x = x;
8306     lvHitTestInfo.pt.y = y;
8307
8308     /* send NM_CLICK notification */
8309     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8310     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8311
8312     /* set left button flag */
8313     infoPtr->bLButtonDown = FALSE;
8314
8315     /* if we clicked on a selected item, edit the label */
8316     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8317         LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8318
8319     return 0;
8320 }
8321
8322 /***
8323  * DESCRIPTION:
8324  * Destroys the listview control (called after WM_DESTROY).
8325  *
8326  * PARAMETER(S):
8327  * [I] infoPtr : valid pointer to the listview structure
8328  *
8329  * RETURN:
8330  * Zero
8331  */
8332 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8333 {
8334   TRACE("()\n");
8335
8336   /* delete all items */
8337   LISTVIEW_DeleteAllItems(infoPtr);
8338
8339   /* destroy data structure */
8340   DPA_Destroy(infoPtr->hdpaItems);
8341   DPA_Destroy(infoPtr->hdpaPosX);
8342   DPA_Destroy(infoPtr->hdpaPosY);
8343   DPA_Destroy(infoPtr->hdpaColumns);
8344   ranges_destroy(infoPtr->selectionRanges);
8345
8346   /* destroy image lists */
8347   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8348   {
8349       if (infoPtr->himlNormal)
8350           ImageList_Destroy(infoPtr->himlNormal);
8351       if (infoPtr->himlSmall)
8352           ImageList_Destroy(infoPtr->himlSmall);
8353       if (infoPtr->himlState)
8354           ImageList_Destroy(infoPtr->himlState);
8355   }
8356
8357   /* destroy font, bkgnd brush */
8358   infoPtr->hFont = 0;
8359   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8360   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8361
8362   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8363
8364   /* free listview info pointer*/
8365   Free(infoPtr);
8366
8367   return 0;
8368 }
8369
8370 /***
8371  * DESCRIPTION:
8372  * Handles notifications from header.
8373  *
8374  * PARAMETER(S):
8375  * [I] infoPtr : valid pointer to the listview structure
8376  * [I] nCtrlId : control identifier
8377  * [I] lpnmh : notification information
8378  *
8379  * RETURN:
8380  * Zero
8381  */
8382 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8383 {
8384     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8385     HWND hwndSelf = infoPtr->hwndSelf;
8386     
8387     TRACE("(lpnmh=%p)\n", lpnmh);
8388
8389     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8390     
8391     switch (lpnmh->hdr.code)
8392     {    
8393         case HDN_ITEMCHANGINGW:
8394         case HDN_ITEMCHANGINGA:
8395             return notify_forward_header(infoPtr, lpnmh);
8396         case HDN_ITEMCHANGEDW:
8397         case HDN_ITEMCHANGEDA:
8398             notify_forward_header(infoPtr, lpnmh);
8399             if (!IsWindow(hwndSelf))
8400                 break;
8401             /* Fall through */
8402         case HDN_TRACKW:
8403         case HDN_TRACKA:
8404         {
8405             COLUMN_INFO *lpColumnInfo;
8406             INT dx, cxy;
8407
8408             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8409             {
8410                 HDITEMW hdi;
8411     
8412                 hdi.mask = HDI_WIDTH;
8413                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8414                 cxy = hdi.cxy;
8415             }
8416             else
8417                 cxy = lpnmh->pitem->cxy;
8418             
8419             /* determine how much we change since the last know position */
8420             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8421             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8422             if (dx != 0)
8423             {
8424                 lpColumnInfo->rcHeader.right += dx;
8425                 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8426                 LISTVIEW_UpdateItemSize(infoPtr);
8427                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8428                 {
8429                     POINT ptOrigin;
8430                     RECT rcCol = lpColumnInfo->rcHeader;
8431                     
8432                     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8433                     OffsetRect(&rcCol, ptOrigin.x, 0);
8434                     
8435                     rcCol.top = infoPtr->rcList.top;
8436                     rcCol.bottom = infoPtr->rcList.bottom;
8437
8438                     /* resizing left-aligned columns leaves most of the left side untouched */
8439                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8440                     {
8441                         INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8442                         rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8443                     }
8444                     
8445                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8446                 }
8447             }
8448         }
8449         break;
8450
8451         case HDN_ITEMCLICKW:
8452         case HDN_ITEMCLICKA:
8453         {
8454             /* Handle sorting by Header Column */
8455             NMLISTVIEW nmlv;
8456
8457             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8458             nmlv.iItem = -1;
8459             nmlv.iSubItem = lpnmh->iItem;
8460             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8461         }
8462         break;
8463
8464         case HDN_DIVIDERDBLCLICKW:
8465         case HDN_DIVIDERDBLCLICKA:
8466             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8467             break;
8468     }
8469
8470     return 0;
8471 }
8472
8473 /***
8474  * DESCRIPTION:
8475  * Paint non-client area of control.
8476  *
8477  * PARAMETER(S):
8478  * [I] infoPtr : valid pointer to the listview structureof the sender
8479  * [I] region : update region
8480  *
8481  * RETURN:
8482  *  TRUE  - frame was painted
8483  *  FALSE - call default window proc
8484  */
8485 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8486 {
8487     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8488     HDC dc;
8489     RECT r;
8490     HRGN cliprgn;
8491     int cxEdge = GetSystemMetrics (SM_CXEDGE),
8492         cyEdge = GetSystemMetrics (SM_CYEDGE);
8493
8494     if (!theme) return FALSE;
8495
8496     GetWindowRect(infoPtr->hwndSelf, &r);
8497
8498     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8499         r.right - cxEdge, r.bottom - cyEdge);
8500     if (region != (HRGN)1)
8501         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8502     OffsetRect(&r, -r.left, -r.top);
8503
8504     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8505     OffsetRect(&r, -r.left, -r.top);
8506
8507     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8508         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8509     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8510     ReleaseDC(infoPtr->hwndSelf, dc);
8511
8512     /* Call default proc to get the scrollbars etc. painted */
8513     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8514
8515     return TRUE;
8516 }
8517
8518 /***
8519  * DESCRIPTION:
8520  * Determines the type of structure to use.
8521  *
8522  * PARAMETER(S):
8523  * [I] infoPtr : valid pointer to the listview structureof the sender
8524  * [I] hwndFrom : listview window handle
8525  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8526  *
8527  * RETURN:
8528  * Zero
8529  */
8530 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8531 {
8532     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8533
8534     if (nCommand != NF_REQUERY) return 0;
8535     
8536     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8537     
8538     return 0;
8539 }
8540
8541 /***
8542  * DESCRIPTION:
8543  * Paints/Repaints the listview control.
8544  *
8545  * PARAMETER(S):
8546  * [I] infoPtr : valid pointer to the listview structure
8547  * [I] hdc : device context handle
8548  *
8549  * RETURN:
8550  * Zero
8551  */
8552 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8553 {
8554     TRACE("(hdc=%p)\n", hdc);
8555
8556     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8557     {
8558         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8559         
8560         infoPtr->bNoItemMetrics = FALSE;
8561         LISTVIEW_UpdateItemSize(infoPtr);
8562         if (uView == LVS_ICON || uView == LVS_SMALLICON)
8563             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8564         LISTVIEW_UpdateScroll(infoPtr);
8565     }
8566     if (hdc) 
8567         LISTVIEW_Refresh(infoPtr, hdc);
8568     else
8569     {
8570         PAINTSTRUCT ps;
8571
8572         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8573         if (!hdc) return 1;
8574         if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8575         LISTVIEW_Refresh(infoPtr, hdc);
8576         EndPaint(infoPtr->hwndSelf, &ps);
8577     }
8578
8579     return 0;
8580 }
8581
8582
8583 /***
8584  * DESCRIPTION:
8585  * Paints/Repaints the listview control.
8586  *
8587  * PARAMETER(S):
8588  * [I] infoPtr : valid pointer to the listview structure
8589  * [I] hdc : device context handle
8590  * [I] options : drawing options
8591  *
8592  * RETURN:
8593  * Zero
8594  */
8595 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8596 {
8597     FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8598
8599     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8600         return 0;
8601
8602     if (options & PRF_ERASEBKGND)
8603         LISTVIEW_EraseBkgnd(infoPtr, hdc);
8604
8605     if (options & PRF_CLIENT)
8606         LISTVIEW_Paint(infoPtr, hdc);
8607
8608     return 0;
8609 }
8610
8611
8612 /***
8613  * DESCRIPTION:
8614  * Processes double click messages (right mouse button).
8615  *
8616  * PARAMETER(S):
8617  * [I] infoPtr : valid pointer to the listview structure
8618  * [I] wKey : key flag
8619  * [I] x,y : mouse coordinate
8620  *
8621  * RETURN:
8622  * Zero
8623  */
8624 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8625 {
8626     LVHITTESTINFO lvHitTestInfo;
8627     
8628     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8629
8630     /* send NM_RELEASEDCAPTURE notification */
8631     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8632
8633     /* send NM_RDBLCLK notification */
8634     lvHitTestInfo.pt.x = x;
8635     lvHitTestInfo.pt.y = y;
8636     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8637     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8638
8639     return 0;
8640 }
8641
8642 /***
8643  * DESCRIPTION:
8644  * Processes mouse down messages (right mouse button).
8645  *
8646  * PARAMETER(S):
8647  * [I] infoPtr : valid pointer to the listview structure
8648  * [I] wKey : key flag
8649  * [I] x,y : mouse coordinate
8650  *
8651  * RETURN:
8652  * Zero
8653  */
8654 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8655 {
8656     LVHITTESTINFO lvHitTestInfo;
8657     INT nItem;
8658
8659     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8660
8661     /* send NM_RELEASEDCAPTURE notification */
8662     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8663
8664     /* make sure the listview control window has the focus */
8665     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8666
8667     /* set right button down flag */
8668     infoPtr->bRButtonDown = TRUE;
8669
8670     /* determine the index of the selected item */
8671     lvHitTestInfo.pt.x = x;
8672     lvHitTestInfo.pt.y = y;
8673     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8674   
8675     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8676     {
8677         LISTVIEW_SetItemFocus(infoPtr, nItem);
8678         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8679             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8680             LISTVIEW_SetSelection(infoPtr, nItem);
8681     }
8682     else
8683     {
8684         LISTVIEW_DeselectAll(infoPtr);
8685     }
8686
8687     return 0;
8688 }
8689
8690 /***
8691  * DESCRIPTION:
8692  * Processes mouse up messages (right mouse button).
8693  *
8694  * PARAMETER(S):
8695  * [I] infoPtr : valid pointer to the listview structure
8696  * [I] wKey : key flag
8697  * [I] x,y : mouse coordinate
8698  *
8699  * RETURN:
8700  * Zero
8701  */
8702 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8703 {
8704     LVHITTESTINFO lvHitTestInfo;
8705     POINT pt;
8706
8707     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8708
8709     if (!infoPtr->bRButtonDown) return 0;
8710  
8711     /* set button flag */
8712     infoPtr->bRButtonDown = FALSE;
8713
8714     /* Send NM_RClICK notification */
8715     lvHitTestInfo.pt.x = x;
8716     lvHitTestInfo.pt.y = y;
8717     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8718     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8719
8720     /* Change to screen coordinate for WM_CONTEXTMENU */
8721     pt = lvHitTestInfo.pt;
8722     ClientToScreen(infoPtr->hwndSelf, &pt);
8723
8724     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8725     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8726                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8727
8728     return 0;
8729 }
8730
8731
8732 /***
8733  * DESCRIPTION:
8734  * Sets the cursor.
8735  *
8736  * PARAMETER(S):
8737  * [I] infoPtr : valid pointer to the listview structure
8738  * [I] hwnd : window handle of window containing the cursor
8739  * [I] nHittest : hit-test code
8740  * [I] wMouseMsg : ideintifier of the mouse message
8741  *
8742  * RETURN:
8743  * TRUE if cursor is set
8744  * FALSE otherwise
8745  */
8746 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8747 {
8748     LVHITTESTINFO lvHitTestInfo;
8749
8750     if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8751
8752     if(!infoPtr->hHotCursor)  return FALSE;
8753
8754     GetCursorPos(&lvHitTestInfo.pt);
8755     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8756
8757     SetCursor(infoPtr->hHotCursor);
8758
8759     return TRUE;
8760 }
8761
8762 /***
8763  * DESCRIPTION:
8764  * Sets the focus.
8765  *
8766  * PARAMETER(S):
8767  * [I] infoPtr : valid pointer to the listview structure
8768  * [I] hwndLoseFocus : handle of previously focused window
8769  *
8770  * RETURN:
8771  * Zero
8772  */
8773 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8774 {
8775     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8776
8777     /* if we have the focus already, there's nothing to do */
8778     if (infoPtr->bFocus) return 0;
8779    
8780     /* send NM_SETFOCUS notification */
8781     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8782
8783     /* set window focus flag */
8784     infoPtr->bFocus = TRUE;
8785
8786     /* put the focus rect back on */
8787     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8788
8789     /* redraw all visible selected items */
8790     LISTVIEW_InvalidateSelectedItems(infoPtr);
8791
8792     return 0;
8793 }
8794
8795 /***
8796  * DESCRIPTION:
8797  * Sets the font.
8798  *
8799  * PARAMETER(S):
8800  * [I] infoPtr : valid pointer to the listview structure
8801  * [I] fRedraw : font handle
8802  * [I] fRedraw : redraw flag
8803  *
8804  * RETURN:
8805  * Zero
8806  */
8807 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8808 {
8809     HFONT oldFont = infoPtr->hFont;
8810
8811     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8812
8813     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8814     if (infoPtr->hFont == oldFont) return 0;
8815     
8816     LISTVIEW_SaveTextMetrics(infoPtr);
8817
8818     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8819         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8820
8821     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8822
8823     return 0;
8824 }
8825
8826 /***
8827  * DESCRIPTION:
8828  * Message handling for WM_SETREDRAW.
8829  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8830  *
8831  * PARAMETER(S):
8832  * [I] infoPtr : valid pointer to the listview structure
8833  * [I] bRedraw: state of redraw flag
8834  *
8835  * RETURN:
8836  * DefWinProc return value
8837  */
8838 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8839 {
8840     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8841
8842     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8843     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8844
8845     infoPtr->bRedraw = bRedraw;
8846
8847     if(!bRedraw) return 0;
8848     
8849     if (is_autoarrange(infoPtr))
8850         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8851     LISTVIEW_UpdateScroll(infoPtr);
8852
8853     /* despite what the WM_SETREDRAW docs says, apps expect us
8854      * to invalidate the listview here... stupid! */
8855     LISTVIEW_InvalidateList(infoPtr);
8856
8857     return 0;
8858 }
8859
8860 /***
8861  * DESCRIPTION:
8862  * Resizes the listview control. This function processes WM_SIZE
8863  * messages.  At this time, the width and height are not used.
8864  *
8865  * PARAMETER(S):
8866  * [I] infoPtr : valid pointer to the listview structure
8867  * [I] Width : new width
8868  * [I] Height : new height
8869  *
8870  * RETURN:
8871  * Zero
8872  */
8873 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8874 {
8875     RECT rcOld = infoPtr->rcList;
8876
8877     TRACE("(width=%d, height=%d)\n", Width, Height);
8878
8879     LISTVIEW_UpdateSize(infoPtr);
8880     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8881   
8882     /* do not bother with display related stuff if we're not redrawing */ 
8883     if (!is_redrawing(infoPtr)) return 0;
8884     
8885     if (is_autoarrange(infoPtr)) 
8886         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8887
8888     LISTVIEW_UpdateScroll(infoPtr);
8889
8890     /* refresh all only for lists whose height changed significantly */
8891     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
8892         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8893         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8894         LISTVIEW_InvalidateList(infoPtr);
8895
8896   return 0;
8897 }
8898
8899 /***
8900  * DESCRIPTION:
8901  * Sets the size information.
8902  *
8903  * PARAMETER(S):
8904  * [I] infoPtr : valid pointer to the listview structure
8905  *
8906  * RETURN:
8907  *  None
8908  */
8909 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8910 {
8911     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8912
8913     TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8914     
8915     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8916
8917     if (uView == LVS_LIST)
8918     {
8919         /* Apparently the "LIST" style is supposed to have the same
8920          * number of items in a column even if there is no scroll bar.
8921          * Since if a scroll bar already exists then the bottom is already
8922          * reduced, only reduce if the scroll bar does not currently exist.
8923          * The "2" is there to mimic the native control. I think it may be
8924          * related to either padding or edges.  (GLA 7/2002)
8925          */
8926         if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8927             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8928         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8929     }
8930     else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8931     {
8932         HDLAYOUT hl;
8933         WINDOWPOS wp;
8934
8935         hl.prc = &infoPtr->rcList;
8936         hl.pwpos = &wp;
8937         Header_Layout(infoPtr->hwndHeader, &hl);
8938
8939         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8940
8941         infoPtr->rcList.top = max(wp.cy, 0);
8942     }
8943
8944     TRACE("  rcList=%s\n", debugrect(&infoPtr->rcList));
8945 }
8946
8947 /***
8948  * DESCRIPTION:
8949  * Processes WM_STYLECHANGED messages.
8950  *
8951  * PARAMETER(S):
8952  * [I] infoPtr : valid pointer to the listview structure
8953  * [I] wStyleType : window style type (normal or extended)
8954  * [I] lpss : window style information
8955  *
8956  * RETURN:
8957  * Zero
8958  */
8959 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8960                                  const STYLESTRUCT *lpss)
8961 {
8962     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8963     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8964
8965     TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8966           wStyleType, lpss->styleOld, lpss->styleNew);
8967
8968     if (wStyleType != GWL_STYLE) return 0;
8969   
8970     /* FIXME: if LVS_NOSORTHEADER changed, update header */
8971     /*        what if LVS_OWNERDATA changed? */
8972     /*        or LVS_SINGLESEL */
8973     /*        or LVS_SORT{AS,DES}CENDING */
8974
8975     infoPtr->dwStyle = lpss->styleNew;
8976
8977     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8978         ((lpss->styleNew & WS_HSCROLL) == 0))
8979        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8980
8981     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8982         ((lpss->styleNew & WS_VSCROLL) == 0))
8983        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8984
8985     if (uNewView != uOldView)
8986     {
8987         SIZE oldIconSize = infoPtr->iconSize;
8988         HIMAGELIST himl;
8989     
8990         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8991         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8992
8993         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8994         SetRectEmpty(&infoPtr->rcFocus);
8995
8996         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8997         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8998     
8999         if (uNewView == LVS_ICON)
9000         {
9001             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9002             {
9003                 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
9004                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9005                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9006             }
9007         }
9008         else if (uNewView == LVS_REPORT)
9009         {
9010             HDLAYOUT hl;
9011             WINDOWPOS wp;
9012
9013             hl.prc = &infoPtr->rcList;
9014             hl.pwpos = &wp;
9015             Header_Layout(infoPtr->hwndHeader, &hl);
9016             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9017         }
9018
9019         LISTVIEW_UpdateItemSize(infoPtr);
9020     }
9021
9022     if (uNewView == LVS_REPORT)
9023         ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9024      
9025     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9026          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9027          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9028
9029     /* update the size of the client area */
9030     LISTVIEW_UpdateSize(infoPtr);
9031
9032     /* add scrollbars if needed */
9033     LISTVIEW_UpdateScroll(infoPtr);
9034
9035     /* invalidate client area + erase background */
9036     LISTVIEW_InvalidateList(infoPtr);
9037
9038     return 0;
9039 }
9040
9041 /***
9042  * DESCRIPTION:
9043  * Window procedure of the listview control.
9044  *
9045  */
9046 static LRESULT WINAPI
9047 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9048 {
9049   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9050
9051   TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9052
9053   if (!infoPtr && (uMsg != WM_CREATE))
9054     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9055
9056   switch (uMsg)
9057   {
9058   case LVM_APPROXIMATEVIEWRECT:
9059     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9060                                         LOWORD(lParam), HIWORD(lParam));
9061   case LVM_ARRANGE:
9062     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9063
9064 /* case LVM_CANCELEDITLABEL: */
9065
9066   case LVM_CREATEDRAGIMAGE:
9067     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9068
9069   case LVM_DELETEALLITEMS:
9070     return LISTVIEW_DeleteAllItems(infoPtr);
9071
9072   case LVM_DELETECOLUMN:
9073     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9074
9075   case LVM_DELETEITEM:
9076     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9077
9078   case LVM_EDITLABELW:
9079     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9080
9081   case LVM_EDITLABELA:
9082     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9083
9084   /* case LVM_ENABLEGROUPVIEW: */
9085
9086   case LVM_ENSUREVISIBLE:
9087     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9088
9089   case LVM_FINDITEMW:
9090     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9091
9092   case LVM_FINDITEMA:
9093     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9094
9095   case LVM_GETBKCOLOR:
9096     return infoPtr->clrBk;
9097
9098   /* case LVM_GETBKIMAGE: */
9099
9100   case LVM_GETCALLBACKMASK:
9101     return infoPtr->uCallbackMask;
9102
9103   case LVM_GETCOLUMNA:
9104     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9105
9106   case LVM_GETCOLUMNW:
9107     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9108
9109   case LVM_GETCOLUMNORDERARRAY:
9110     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9111
9112   case LVM_GETCOLUMNWIDTH:
9113     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9114
9115   case LVM_GETCOUNTPERPAGE:
9116     return LISTVIEW_GetCountPerPage(infoPtr);
9117
9118   case LVM_GETEDITCONTROL:
9119     return (LRESULT)infoPtr->hwndEdit;
9120
9121   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9122     return infoPtr->dwLvExStyle;
9123
9124   /* case LVM_GETGROUPINFO: */
9125
9126   /* case LVM_GETGROUPMETRICS: */
9127
9128   case LVM_GETHEADER:
9129     return (LRESULT)infoPtr->hwndHeader;
9130
9131   case LVM_GETHOTCURSOR:
9132     return (LRESULT)infoPtr->hHotCursor;
9133
9134   case LVM_GETHOTITEM:
9135     return infoPtr->nHotItem;
9136
9137   case LVM_GETHOVERTIME:
9138     return infoPtr->dwHoverTime;
9139
9140   case LVM_GETIMAGELIST:
9141     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9142
9143   /* case LVM_GETINSERTMARK: */
9144
9145   /* case LVM_GETINSERTMARKCOLOR: */
9146
9147   /* case LVM_GETINSERTMARKRECT: */
9148
9149   case LVM_GETISEARCHSTRINGA:
9150   case LVM_GETISEARCHSTRINGW:
9151     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9152     return FALSE;
9153
9154   case LVM_GETITEMA:
9155     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9156
9157   case LVM_GETITEMW:
9158     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9159
9160   case LVM_GETITEMCOUNT:
9161     return infoPtr->nItemCount;
9162
9163   case LVM_GETITEMPOSITION:
9164     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9165
9166   case LVM_GETITEMRECT:
9167     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9168
9169   case LVM_GETITEMSPACING:
9170     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9171
9172   case LVM_GETITEMSTATE:
9173     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9174
9175   case LVM_GETITEMTEXTA:
9176     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9177
9178   case LVM_GETITEMTEXTW:
9179     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9180
9181   case LVM_GETNEXTITEM:
9182     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9183
9184   case LVM_GETNUMBEROFWORKAREAS:
9185     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9186     return 1;
9187
9188   case LVM_GETORIGIN:
9189     if (!lParam) return FALSE;
9190     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9191     return TRUE;
9192
9193   /* case LVM_GETOUTLINECOLOR: */
9194
9195   /* case LVM_GETSELECTEDCOLUMN: */
9196
9197   case LVM_GETSELECTEDCOUNT:
9198     return LISTVIEW_GetSelectedCount(infoPtr);
9199
9200   case LVM_GETSELECTIONMARK:
9201     return infoPtr->nSelectionMark;
9202
9203   case LVM_GETSTRINGWIDTHA:
9204     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9205
9206   case LVM_GETSTRINGWIDTHW:
9207     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9208
9209   case LVM_GETSUBITEMRECT:
9210     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9211
9212   case LVM_GETTEXTBKCOLOR:
9213     return infoPtr->clrTextBk;
9214
9215   case LVM_GETTEXTCOLOR:
9216     return infoPtr->clrText;
9217
9218   /* case LVM_GETTILEINFO: */
9219
9220   /* case LVM_GETTILEVIEWINFO: */
9221
9222   case LVM_GETTOOLTIPS:
9223     if( !infoPtr->hwndToolTip )
9224         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9225     return (LRESULT)infoPtr->hwndToolTip;
9226
9227   case LVM_GETTOPINDEX:
9228     return LISTVIEW_GetTopIndex(infoPtr);
9229
9230   /*case LVM_GETUNICODEFORMAT:
9231     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9232     return FALSE;*/
9233
9234   /* case LVM_GETVIEW: */
9235
9236   case LVM_GETVIEWRECT:
9237     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9238
9239   case LVM_GETWORKAREAS:
9240     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9241     return FALSE;
9242
9243   /* case LVM_HASGROUP: */
9244
9245   case LVM_HITTEST:
9246     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9247
9248   case LVM_INSERTCOLUMNA:
9249     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9250
9251   case LVM_INSERTCOLUMNW:
9252     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9253
9254   /* case LVM_INSERTGROUP: */
9255
9256   /* case LVM_INSERTGROUPSORTED: */
9257
9258   case LVM_INSERTITEMA:
9259     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9260
9261   case LVM_INSERTITEMW:
9262     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9263
9264   /* case LVM_INSERTMARKHITTEST: */
9265
9266   /* case LVM_ISGROUPVIEWENABLED: */
9267
9268   /* case LVM_MAPIDTOINDEX: */
9269
9270   /* case LVM_MAPINDEXTOID: */
9271
9272   /* case LVM_MOVEGROUP: */
9273
9274   /* case LVM_MOVEITEMTOGROUP: */
9275
9276   case LVM_REDRAWITEMS:
9277     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9278
9279   /* case LVM_REMOVEALLGROUPS: */
9280
9281   /* case LVM_REMOVEGROUP: */
9282
9283   case LVM_SCROLL:
9284     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9285
9286   case LVM_SETBKCOLOR:
9287     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9288
9289   /* case LVM_SETBKIMAGE: */
9290
9291   case LVM_SETCALLBACKMASK:
9292     infoPtr->uCallbackMask = (UINT)wParam;
9293     return TRUE;
9294
9295   case LVM_SETCOLUMNA:
9296     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9297
9298   case LVM_SETCOLUMNW:
9299     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9300
9301   case LVM_SETCOLUMNORDERARRAY:
9302     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9303
9304   case LVM_SETCOLUMNWIDTH:
9305     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9306
9307   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9308     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9309
9310   /* case LVM_SETGROUPINFO: */
9311
9312   /* case LVM_SETGROUPMETRICS: */
9313
9314   case LVM_SETHOTCURSOR:
9315     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9316
9317   case LVM_SETHOTITEM:
9318     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9319
9320   case LVM_SETHOVERTIME:
9321     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9322
9323   case LVM_SETICONSPACING:
9324     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9325
9326   case LVM_SETIMAGELIST:
9327     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9328
9329   /* case LVM_SETINFOTIP: */
9330
9331   /* case LVM_SETINSERTMARK: */
9332
9333   /* case LVM_SETINSERTMARKCOLOR: */
9334
9335   case LVM_SETITEMA:
9336     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9337
9338   case LVM_SETITEMW:
9339     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9340
9341   case LVM_SETITEMCOUNT:
9342     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9343
9344   case LVM_SETITEMPOSITION:
9345     {
9346         POINT pt;
9347         pt.x = (short)LOWORD(lParam);
9348         pt.y = (short)HIWORD(lParam);
9349         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9350     }
9351
9352   case LVM_SETITEMPOSITION32:
9353     if (lParam == 0) return FALSE;
9354     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9355
9356   case LVM_SETITEMSTATE:
9357     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9358
9359   case LVM_SETITEMTEXTA:
9360     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9361
9362   case LVM_SETITEMTEXTW:
9363     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9364
9365   /* case LVM_SETOUTLINECOLOR: */
9366
9367   /* case LVM_SETSELECTEDCOLUMN: */
9368
9369   case LVM_SETSELECTIONMARK:
9370     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9371
9372   case LVM_SETTEXTBKCOLOR:
9373     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9374
9375   case LVM_SETTEXTCOLOR:
9376     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9377
9378   /* case LVM_SETTILEINFO: */
9379
9380   /* case LVM_SETTILEVIEWINFO: */
9381
9382   /* case LVM_SETTILEWIDTH: */
9383
9384   case LVM_SETTOOLTIPS:
9385     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9386
9387   /* case LVM_SETUNICODEFORMAT: */
9388
9389   /* case LVM_SETVIEW: */
9390
9391   /* case LVM_SETWORKAREAS: */
9392
9393   /* case LVM_SORTGROUPS: */
9394
9395   case LVM_SORTITEMS:
9396     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9397
9398   /* LVM_SORTITEMSEX: */
9399
9400   case LVM_SUBITEMHITTEST:
9401     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9402
9403   case LVM_UPDATE:
9404     return LISTVIEW_Update(infoPtr, (INT)wParam);
9405
9406   case WM_CHAR:
9407     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9408
9409   case WM_COMMAND:
9410     return LISTVIEW_Command(infoPtr, wParam, lParam);
9411
9412   case WM_CREATE:
9413     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9414
9415   case WM_DESTROY:
9416     return LISTVIEW_Destroy(infoPtr);
9417
9418   case WM_ENABLE:
9419     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9420
9421   case WM_ERASEBKGND:
9422     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9423
9424   case WM_GETDLGCODE:
9425     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9426
9427   case WM_GETFONT:
9428     return (LRESULT)infoPtr->hFont;
9429
9430   case WM_HSCROLL:
9431     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9432
9433   case WM_KEYDOWN:
9434     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9435
9436   case WM_KILLFOCUS:
9437     return LISTVIEW_KillFocus(infoPtr);
9438
9439   case WM_LBUTTONDBLCLK:
9440     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9441
9442   case WM_LBUTTONDOWN:
9443     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9444
9445   case WM_LBUTTONUP:
9446     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9447
9448   case WM_MOUSEMOVE:
9449     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9450
9451   case WM_MOUSEHOVER:
9452     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9453
9454   case WM_NCDESTROY:
9455     return LISTVIEW_NCDestroy(infoPtr);
9456
9457   case WM_NCPAINT:
9458     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9459         return 0;
9460     goto fwd_msg;
9461
9462   case WM_NOTIFY:
9463     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9464         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9465     else return 0;
9466
9467   case WM_NOTIFYFORMAT:
9468     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9469
9470   case WM_PRINTCLIENT:
9471     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9472
9473   case WM_PAINT:
9474     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9475
9476   case WM_RBUTTONDBLCLK:
9477     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9478
9479   case WM_RBUTTONDOWN:
9480     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9481
9482   case WM_RBUTTONUP:
9483     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9484
9485   case WM_SETCURSOR:
9486     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9487       return TRUE;
9488     goto fwd_msg;
9489
9490   case WM_SETFOCUS:
9491     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9492
9493   case WM_SETFONT:
9494     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9495
9496   case WM_SETREDRAW:
9497     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9498
9499   case WM_SIZE:
9500     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9501
9502   case WM_STYLECHANGED:
9503     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9504
9505   case WM_SYSCOLORCHANGE:
9506     COMCTL32_RefreshSysColors();
9507     return 0;
9508
9509 /*      case WM_TIMER: */
9510   case WM_THEMECHANGED:
9511     return LISTVIEW_ThemeChanged(infoPtr);
9512
9513   case WM_VSCROLL:
9514     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9515
9516   case WM_MOUSEWHEEL:
9517       if (wParam & (MK_SHIFT | MK_CONTROL))
9518           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9519       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9520
9521   case WM_WINDOWPOSCHANGED:
9522       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
9523       {
9524       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9525           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9526                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9527
9528       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9529       {
9530           MEASUREITEMSTRUCT mis;
9531           mis.CtlType = ODT_LISTVIEW;
9532           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9533           mis.itemID = -1;
9534           mis.itemWidth = 0;
9535           mis.itemData = 0;
9536           mis.itemHeight= infoPtr->nItemHeight;
9537           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9538           if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9539               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9540       }
9541
9542           LISTVIEW_UpdateSize(infoPtr);
9543           LISTVIEW_UpdateScroll(infoPtr);
9544       }
9545       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9546
9547 /*      case WM_WININICHANGE: */
9548
9549   default:
9550     if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9551       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9552
9553   fwd_msg:
9554     /* call default window procedure */
9555     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9556   }
9557
9558 }
9559
9560 /***
9561  * DESCRIPTION:
9562  * Registers the window class.
9563  *
9564  * PARAMETER(S):
9565  * None
9566  *
9567  * RETURN:
9568  * None
9569  */
9570 void LISTVIEW_Register(void)
9571 {
9572     WNDCLASSW wndClass;
9573
9574     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9575     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9576     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9577     wndClass.cbClsExtra = 0;
9578     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9579     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9580     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9581     wndClass.lpszClassName = WC_LISTVIEWW;
9582     RegisterClassW(&wndClass);
9583 }
9584
9585 /***
9586  * DESCRIPTION:
9587  * Unregisters the window class.
9588  *
9589  * PARAMETER(S):
9590  * None
9591  *
9592  * RETURN:
9593  * None
9594  */
9595 void LISTVIEW_Unregister(void)
9596 {
9597     UnregisterClassW(WC_LISTVIEWW, NULL);
9598 }
9599
9600 /***
9601  * DESCRIPTION:
9602  * Handle any WM_COMMAND messages
9603  *
9604  * PARAMETER(S):
9605  * [I] infoPtr : valid pointer to the listview structure
9606  * [I] wParam : the first message parameter
9607  * [I] lParam : the second message parameter
9608  *
9609  * RETURN:
9610  *   Zero.
9611  */
9612 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9613 {
9614     switch (HIWORD(wParam))
9615     {
9616         case EN_UPDATE:
9617         {
9618             /*
9619              * Adjust the edit window size
9620              */
9621             WCHAR buffer[1024];
9622             HDC           hdc = GetDC(infoPtr->hwndEdit);
9623             HFONT         hFont, hOldFont = 0;
9624             RECT          rect;
9625             SIZE          sz;
9626             int           len;
9627
9628             if (!infoPtr->hwndEdit || !hdc) return 0;
9629             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9630             GetWindowRect(infoPtr->hwndEdit, &rect);
9631
9632             /* Select font to get the right dimension of the string */
9633             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9634             if(hFont != 0)
9635             {
9636                 hOldFont = SelectObject(hdc, hFont);
9637             }
9638
9639             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9640             {
9641                 TEXTMETRICW textMetric;
9642
9643                 /* Add Extra spacing for the next character */
9644                 GetTextMetricsW(hdc, &textMetric);
9645                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9646
9647                 SetWindowPos (
9648                     infoPtr->hwndEdit,
9649                     HWND_TOP,
9650                     0,
9651                     0,
9652                     sz.cx,
9653                     rect.bottom - rect.top,
9654                     SWP_DRAWFRAME|SWP_NOMOVE);
9655             }
9656             if(hFont != 0)
9657                 SelectObject(hdc, hOldFont);
9658
9659             ReleaseDC(infoPtr->hwndEdit, hdc);
9660
9661             break;
9662         }
9663
9664         default:
9665           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9666     }
9667
9668     return 0;
9669 }
9670
9671
9672 /***
9673  * DESCRIPTION:
9674  * Subclassed edit control windproc function
9675  *
9676  * PARAMETER(S):
9677  * [I] hwnd : the edit window handle
9678  * [I] uMsg : the message that is to be processed
9679  * [I] wParam : first message parameter
9680  * [I] lParam : second message parameter
9681  * [I] isW : TRUE if input is Unicode
9682  *
9683  * RETURN:
9684  *   Zero.
9685  */
9686 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9687 {
9688     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9689     BOOL cancel = FALSE;
9690
9691     TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9692           hwnd, uMsg, wParam, lParam, isW);
9693
9694     switch (uMsg)
9695     {
9696         case WM_GETDLGCODE:
9697           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9698
9699         case WM_KILLFOCUS:
9700             break;
9701
9702         case WM_DESTROY:
9703         {
9704             WNDPROC editProc = infoPtr->EditWndProc;
9705             infoPtr->EditWndProc = 0;
9706             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9707             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9708         }
9709
9710         case WM_KEYDOWN:
9711             if (VK_ESCAPE == (INT)wParam)
9712             {
9713                 cancel = TRUE;
9714                 break;
9715             }
9716             else if (VK_RETURN == (INT)wParam)
9717                 break;
9718
9719         default:
9720             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9721     }
9722
9723     /* kill the edit */
9724     if (infoPtr->hwndEdit)
9725     {
9726         LPWSTR buffer = NULL;
9727
9728         infoPtr->hwndEdit = 0;
9729         if (!cancel)
9730         {
9731             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9732
9733             if (len)
9734             {
9735                 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9736                 {
9737                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
9738                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9739                 }
9740             }
9741         }
9742         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9743
9744         if (buffer) Free(buffer);
9745
9746     }
9747
9748     SendMessageW(hwnd, WM_CLOSE, 0, 0);
9749     return 0;
9750 }
9751
9752 /***
9753  * DESCRIPTION:
9754  * Subclassed edit control Unicode windproc function
9755  *
9756  * PARAMETER(S):
9757  * [I] hwnd : the edit window handle
9758  * [I] uMsg : the message that is to be processed
9759  * [I] wParam : first message parameter
9760  * [I] lParam : second message parameter
9761  *
9762  * RETURN:
9763  */
9764 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9765 {
9766     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9767 }
9768
9769 /***
9770  * DESCRIPTION:
9771  * Subclassed edit control ANSI windproc function
9772  *
9773  * PARAMETER(S):
9774  * [I] hwnd : the edit window handle
9775  * [I] uMsg : the message that is to be processed
9776  * [I] wParam : first message parameter
9777  * [I] lParam : second message parameter
9778  *
9779  * RETURN:
9780  */
9781 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9782 {
9783     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9784 }
9785
9786 /***
9787  * DESCRIPTION:
9788  * Creates a subclassed edit cotrol
9789  *
9790  * PARAMETER(S):
9791  * [I] infoPtr : valid pointer to the listview structure
9792  * [I] text : initial text for the edit
9793  * [I] style : the window style
9794  * [I] isW : TRUE if input is Unicode
9795  *
9796  * RETURN:
9797  */
9798 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9799         INT x, INT y, INT width, INT height, BOOL isW)
9800 {
9801     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9802     HWND hedit;
9803     SIZE sz;
9804     HDC hdc;
9805     HDC hOldFont=0;
9806     TEXTMETRICW textMetric;
9807     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9808
9809     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9810
9811     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9812     hdc = GetDC(infoPtr->hwndSelf);
9813
9814     /* Select the font to get appropriate metric dimensions */
9815     if(infoPtr->hFont != 0)
9816         hOldFont = SelectObject(hdc, infoPtr->hFont);
9817
9818     /*Get String Length in pixels */
9819     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9820
9821     /*Add Extra spacing for the next character */
9822     GetTextMetricsW(hdc, &textMetric);
9823     sz.cx += (textMetric.tmMaxCharWidth * 2);
9824
9825     if(infoPtr->hFont != 0)
9826         SelectObject(hdc, hOldFont);
9827
9828     ReleaseDC(infoPtr->hwndSelf, hdc);
9829     if (isW)
9830         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9831     else
9832         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9833
9834     if (!hedit) return 0;
9835
9836     infoPtr->EditWndProc = (WNDPROC)
9837         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9838                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9839
9840     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9841
9842     return hedit;
9843 }