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