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