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