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