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