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