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