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