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