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