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