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