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