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