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