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