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