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