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