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