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