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