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