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