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