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