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