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