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