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