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