[COMCTL32]
[reactos.git] / reactos / dll / win32 / comctl32 / listview.c
index 266d397..73d0fb5 100644 (file)
@@ -6,8 +6,9 @@
  * Copyright 2000 Jason Mawdsley
  * Copyright 2001 CodeWeavers Inc.
  * Copyright 2002 Dimitrie O. Paun
- * Copyright 2009-2011 Nikolay Sivov
+ * Copyright 2009-2014 Nikolay Sivov
  * Copyright 2009 Owen Rudge for CodeWeavers
+ * Copyright 2012-2013 Daniel Jelinski
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -54,7 +55,6 @@
  *   -- LVA_SNAPTOGRID not implemented
  *   -- LISTVIEW_ApproximateViewRect partially implemented
  *   -- LISTVIEW_SetColumnWidth ignores header images & bitmap
- *   -- LISTVIEW_SetIconSpacing is incomplete
  *   -- LISTVIEW_StyleChanged doesn't handle some changes too well
  *
  * Speedups
@@ -75,7 +75,6 @@
  * States
  *   -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
  *   -- LVIS_DROPHILITED
- *   -- LVIS_OVERLAYMASK
  *
  * Styles
  *   -- LVS_NOLABELWRAP
@@ -99,7 +98,6 @@
  *   -- LVN_GETINFOTIP
  *   -- LVN_HOTTRACK
  *   -- LVN_SETDISPINFO
- *   -- LVN_BEGINRDRAG
  *
  * Messages:
  *   -- LVM_ENABLEGROUPVIEW
  *
  * Functions:
  *   -- LVGroupComparE
- *
- * Known differences in message stream from native control (not known if
- * these differences cause problems):
- *   LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
- *   LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
- *   WM_CREATE does not issue WM_QUERYUISTATE and associated registry
- *     processing for "USEDOUBLECLICKTIME".
  */
 
-#include "config.h"
-#include "wine/port.h"
-
-#include <assert.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "windef.h"
-#include "winbase.h"
-#include "winnt.h"
-#include "wingdi.h"
-#include "winuser.h"
-#include "winnls.h"
-#include "commctrl.h"
 #include "comctl32.h"
-#include "uxtheme.h"
 
-#include "wine/debug.h"
-#include "wine/unicode.h"
+#include <stdio.h>
 
 WINE_DEFAULT_DEBUG_CHANNEL(listview);
 
@@ -290,6 +262,7 @@ typedef struct tagLISTVIEW_INFO
   HIMAGELIST himlSmall;
   HIMAGELIST himlState;
   SIZE iconSize;
+  BOOL autoSpacing;
   SIZE iconSpacing;
   SIZE iconStateSize;
   POINT currIconPos;        /* this is the position next icon will be placed */
@@ -326,12 +299,12 @@ typedef struct tagLISTVIEW_INFO
 
   /* mouse operation */
   BOOL bLButtonDown;
-  BOOL bRButtonDown;
   BOOL bDragging;
   POINT ptClickPos;         /* point where the user clicked */
   INT nLButtonDownItem;     /* tracks item to reset multiselection on WM_LBUTTONUP */
   DWORD dwHoverTime;
   HCURSOR hHotCursor;
+  INT cWheelRemainder;
 
   /* keyboard operation */
   DWORD lastKeyPressTimestamp;
@@ -340,7 +313,6 @@ typedef struct tagLISTVIEW_INFO
   WCHAR szSearchParam[ MAX_PATH ];
 
   /* painting */
-  DWORD cditemmode;        /* Keep the custom draw flags for an item/row */
   BOOL bIsDrawing;         /* Drawing in progress */
   INT nMeasureItemHeight;  /* WM_MEASUREITEM result */
   BOOL bRedraw;            /* WM_SETREDRAW switch */
@@ -560,11 +532,8 @@ static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
     
 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
 {
-    int res;
-
     n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
-    res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
-    return res ? res - sizeof(WCHAR) : res;
+    return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n) - CSTR_EQUAL;
 }
 
 /******** Debugging functions *****************************************/
@@ -778,13 +747,11 @@ static int get_ansi_notification(UINT unicodeNotificationCode)
 }
 
 /* forwards header notifications to listview parent */
-static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
+static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, NMHEADERW *lpnmhW)
 {
-    NMHEADERA nmhA;
-    HDITEMA hditema;
-    HD_TEXTFILTERA textfilter;
-    LPSTR text = NULL, filter = NULL;
+    LPCWSTR text = NULL, filter = NULL;
     LRESULT ret;
+    NMHEADERA *lpnmh = (NMHEADERA*) lpnmhW;
 
     /* on unicode format exit earlier */
     if (infoPtr->notifyFormat == NFR_UNICODE)
@@ -793,37 +760,40 @@ static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADE
 
     /* header always supplies unicode notifications,
        all we have to do is to convert strings to ANSI */
-    nmhA = *(const NMHEADERA*)lpnmh;
     if (lpnmh->pitem)
     {
-        hditema = *(HDITEMA*)lpnmh->pitem;
-        nmhA.pitem = &hditema;
         /* convert item text */
         if (lpnmh->pitem->mask & HDI_TEXT)
         {
-            hditema.pszText = NULL;
-            Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
-            text = hditema.pszText;
+            text = (LPCWSTR)lpnmh->pitem->pszText;
+            lpnmh->pitem->pszText = NULL;
+            Str_SetPtrWtoA(&lpnmh->pitem->pszText, text);
         }
         /* convert filter text */
         if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
              lpnmh->pitem->pvFilter)
         {
-            hditema.pvFilter = &textfilter;
-            textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
-            textfilter.pszText = NULL;
-            Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
-            filter = textfilter.pszText;
+            filter = (LPCWSTR)((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText;
+            ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = NULL;
+            Str_SetPtrWtoA(&((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText, filter);
         }
     }
-    nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
+    lpnmh->hdr.code = get_ansi_notification(lpnmh->hdr.code);
 
-    ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhA.hdr.idFrom,
-                       (LPARAM)&nmhA);
+    ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
+                       (LPARAM)lpnmh);
 
     /* cleanup */
-    Free(text);
-    Free(filter);
+    if(text)
+    {
+        Free(lpnmh->pitem->pszText);
+        lpnmh->pitem->pszText = (LPSTR)text;
+    }
+    if(filter)
+    {
+        Free(((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText);
+        ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = (LPSTR)filter;
+    }
 
     return ret;
 }
@@ -891,11 +861,13 @@ static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LP
     return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
 }
 
+/* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
 {
     NMITEMACTIVATE nmia;
     LVITEMW item;
     HWND hwnd = infoPtr->hwndSelf;
+    LRESULT ret;
 
     TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht)); 
     ZeroMemory(&nmia, sizeof(nmia));
@@ -906,8 +878,8 @@ static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTEST
     item.iItem = lvht->iItem;
     item.iSubItem = 0;
     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
-    notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
-    return IsWindow(hwnd);
+    ret = notify_hdr(infoPtr, code, (NMHDR*)&nmia);
+    return IsWindow(hwnd) && (code == NM_RCLICK ? !ret : TRUE);
 }
 
 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
@@ -1063,10 +1035,7 @@ static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDra
 
 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
 {
-    if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
-        lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
-    if (lpnmlvcd->clrText == CLR_DEFAULT)
-        lpnmlvcd->clrText = comctl32_color.clrWindowText;
+    COLORREF backcolor, textcolor;
 
     /* apparently, for selected items, we have to override the returned values */
     if (!SubItem)
@@ -1086,15 +1055,23 @@ static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRA
         }
     }
 
+    backcolor = lpnmlvcd->clrTextBk;
+    textcolor = lpnmlvcd->clrText;
+
+    if (backcolor == CLR_DEFAULT)
+        backcolor = comctl32_color.clrWindow;
+    if (textcolor == CLR_DEFAULT)
+        textcolor = comctl32_color.clrWindowText;
+
     /* Set the text attributes */
-    if (lpnmlvcd->clrTextBk != CLR_NONE)
+    if (backcolor != CLR_NONE)
     {
        SetBkMode(hdc, OPAQUE);
-       SetBkColor(hdc,lpnmlvcd->clrTextBk);
+       SetBkColor(hdc, backcolor);
     }
     else
        SetBkMode(hdc, TRANSPARENT);
-    SetTextColor(hdc, lpnmlvcd->clrText);
+    SetTextColor(hdc, textcolor);
 }
 
 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
@@ -1147,7 +1124,7 @@ static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
  * ITERATOR DOCUMENTATION
  *
  * The iterator functions allow for easy, and convenient iteration
- * over items of interest in the list. Typically, you create a
+ * over items of interest in the list. Typically, you create an
  * iterator, use it, and destroy it, as such:
  *   ITERATOR i;
  *
@@ -1675,6 +1652,10 @@ static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
     /* set header font */
     SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, TRUE);
 
+    /* set header image list */
+    if (infoPtr->himlSmall)
+        SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)infoPtr->himlSmall);
+
     LISTVIEW_UpdateSize(infoPtr);
 
     return 0;
@@ -1850,9 +1831,9 @@ static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
 {
     WCHAR buffer[MAX_PATH];
-    INT endidx, startidx;
     DWORD prevTime;
     LVITEMW item;
+    int startidx;
     INT nItem;
     INT diff;
 
@@ -1892,15 +1873,16 @@ static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, L
         infoPtr->nSearchParamLength = 1;
     }
 
-    /* and search from the current position */
-    nItem = -1;
-    endidx = infoPtr->nItemCount;
-
     /* should start from next after focused item, so next item that matches
        will be selected, if there isn't any and focused matches it will be selected
        on second search stage from beginning of the list */
     if (infoPtr->nFocusedItem >= 0 && infoPtr->nItemCount > 1)
-        startidx = infoPtr->nFocusedItem + 1;
+    {
+        /* with some accumulated search data available start with current focus, otherwise
+           it's excluded from search */
+        startidx = infoPtr->nSearchParamLength > 1 ? infoPtr->nFocusedItem : infoPtr->nFocusedItem + 1;
+        if (startidx == infoPtr->nItemCount) startidx = 0;
+    }
     else
         startidx = 0;
 
@@ -1920,7 +1902,11 @@ static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, L
     }
     else
     {
-        INT i = startidx;
+        int i = startidx, endidx;
+
+        /* and search from the current position */
+        nItem = -1;
+        endidx = infoPtr->nItemCount;
 
         /* first search in [startidx, endidx), on failure continue in [0, startidx) */
         while (1)
@@ -1942,12 +1928,15 @@ static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, L
                 item.cchTextMax = MAX_PATH;
                 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
 
-                if (lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength) == 0)
+                if (!lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength))
                 {
                     nItem = i;
                     break;
                 }
-                else if (nItem == -1 && lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1) == 0)
+                /* this is used to find first char match when search string is not available yet,
+                   otherwise every WM_CHAR will search to next item by first char, ignoring that we're
+                   already waiting for user to complete a string */
+                else if (nItem == -1 && infoPtr->nSearchParamLength == 1 && !lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1))
                 {
                     /* this would work but we must keep looking for a longer match */
                     nItem = i;
@@ -2379,7 +2368,7 @@ static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW
            Icon.top    = Box.top;
            Icon.right  = Icon.left;
            if (infoPtr->himlSmall &&
-                (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
+                (!lpColumnInfo || lpLVItem->iSubItem == 0 ||
                  ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
                Icon.right += infoPtr->iconSize.cx;
            Icon.bottom = Icon.top + infoPtr->iconSize.cy;
@@ -2970,15 +2959,12 @@ static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
        nItemHeight = infoPtr->iconSpacing.cy;
     else
     {
-       nItemHeight = infoPtr->ntmHeight; 
-        if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
-            nItemHeight++;
+       nItemHeight = infoPtr->ntmHeight;
        if (infoPtr->himlState)
            nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
        if (infoPtr->himlSmall)
            nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
-       if (infoPtr->himlState || infoPtr->himlSmall)
-           nItemHeight += HEIGHT_PADDING;
+       nItemHeight += HEIGHT_PADDING;
     if (infoPtr->nMeasureItemHeight > 0)
         nItemHeight = infoPtr->nMeasureItemHeight;
     }
@@ -3462,7 +3448,6 @@ static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
     return oldFocus != infoPtr->nFocusedItem;
 }
 
-/* Helper function for LISTVIEW_ShiftIndices *only* */
 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
 {
     if (nShiftItem < nItem) return nShiftItem;
@@ -3474,6 +3459,24 @@ static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, I
     return min(nShiftItem, infoPtr->nItemCount - 1);
 }
 
+/* This function updates focus index.
+
+Parameters:
+   focus : current focus index
+   item : index of item to be added/removed
+   direction : add/remove flag
+*/
+static void LISTVIEW_ShiftFocus(LISTVIEW_INFO *infoPtr, INT focus, INT item, INT direction)
+{
+    BOOL old_change = infoPtr->bDoChangeNotify;
+
+    infoPtr->bDoChangeNotify = FALSE;
+    focus = shift_item(infoPtr, focus, item, direction);
+    if (focus != infoPtr->nFocusedItem)
+        LISTVIEW_SetItemFocus(infoPtr, focus);
+    infoPtr->bDoChangeNotify = old_change;
+}
+
 /**
 * DESCRIPTION:
 * Updates the various indices after an item has been inserted or deleted.
@@ -3488,31 +3491,15 @@ static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, I
 */
 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
 {
-    INT nNewFocus;
-    BOOL bOldChange;
-
-    /* temporarily disable change notification while shifting items */
-    bOldChange = infoPtr->bDoChangeNotify;
-    infoPtr->bDoChangeNotify = FALSE;
-
-    TRACE("Shifting %iu, %i steps\n", nItem, direction);
+    TRACE("Shifting %i, %i steps\n", nItem, direction);
 
     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
-
     assert(abs(direction) == 1);
-
     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
 
-    nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
-    if (nNewFocus != infoPtr->nFocusedItem)
-        LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
-    
     /* But we are not supposed to modify nHotItem! */
-
-    infoPtr->bDoChangeNotify = bOldChange;
 }
 
-
 /**
  * DESCRIPTION:
  * Adds a block of selections.
@@ -3553,8 +3540,8 @@ static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
     ZeroMemory(&nmlv, sizeof(nmlv));
     nmlv.iFrom = nFirst;
     nmlv.iTo = nLast;
-    nmlv.uNewState = 0;
-    nmlv.uOldState = item.state;
+    nmlv.uOldState = 0;
+    nmlv.uNewState = item.state;
 
     notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
     if (!IsWindow(hwndSelf))
@@ -3609,9 +3596,15 @@ static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
        POINT ptItem;
        
        rcItem.left = LVIR_BOUNDS;
-       if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
+       if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) {
+            ranges_destroy (selection);
+            return;
+       }
        rcSelMark.left = LVIR_BOUNDS;
-       if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
+       if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) {
+            ranges_destroy (selection);
+            return;
+       }
        UnionRect(&rcSel, &rcItem, &rcSelMark);
        iterator_frameditems(&i, infoPtr, &rcSel);
        while(iterator_next(&i))
@@ -3683,8 +3676,8 @@ static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
 {
   /* FIXME: pass in the state */
-  WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
-  WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
+  WORD wShift = GetKeyState(VK_SHIFT) & 0x8000;
+  WORD wCtrl = GetKeyState(VK_CONTROL) & 0x8000;
   BOOL bResult = FALSE;
 
   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
@@ -3901,7 +3894,7 @@ static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, const POINT *coord
     }
     iterator_destroy(&new_elems);
 
-    LISTVIEW_InvalidateRect(infoPtr, &rect);
+    LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
 }
 
 /***
@@ -3992,17 +3985,23 @@ static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent
  */
 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
 {
+    LVHITTESTINFO ht;
+    RECT rect;
+    POINT pt;
+
     if (!(fwKeys & MK_LBUTTON))
         infoPtr->bLButtonDown = FALSE;
 
     if (infoPtr->bLButtonDown)
     {
-        POINT tmp;
-        RECT rect;
-        LVHITTESTINFO lvHitTestInfo;
-        WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
-        WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
+        rect.left = rect.right = infoPtr->ptClickPos.x;
+        rect.top = rect.bottom = infoPtr->ptClickPos.y;
+
+        InflateRect(&rect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
+    }
 
+    if (infoPtr->bLButtonDown)
+    {
         if (infoPtr->bMarqueeSelect)
         {
             POINT coords_orig;
@@ -4045,22 +4044,17 @@ static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, IN
             return 0;
         }
 
-        rect.left = infoPtr->ptClickPos.x - wDragWidth;
-        rect.right = infoPtr->ptClickPos.x + wDragWidth;
-        rect.top = infoPtr->ptClickPos.y - wDragHeight;
-        rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
-
-        tmp.x = x;
-        tmp.y = y;
+        pt.x = x;
+        pt.y = y;
 
-        lvHitTestInfo.pt = tmp;
-        LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
+        ht.pt = pt;
+        LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
 
         /* reset item marker */
-        if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
+        if (infoPtr->nLButtonDownItem != ht.iItem)
             infoPtr->nLButtonDownItem = -1;
 
-        if (!PtInRect(&rect, tmp))
+        if (!PtInRect(&rect, pt))
         {
             /* this path covers the following:
                1. WM_LBUTTONDOWN over selected item (sets focus on it)
@@ -4080,12 +4074,12 @@ static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, IN
 
             if (!infoPtr->bDragging)
             {
-                lvHitTestInfo.pt = infoPtr->ptClickPos;
-                LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
+                ht.pt = infoPtr->ptClickPos;
+                LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
 
                 /* If the click is outside the range of an item, begin a
                    highlight. If not, begin an item drag. */
-                if (lvHitTestInfo.iItem == -1)
+                if (ht.iItem == -1)
                 {
                     NMHDR hdr;
 
@@ -4110,7 +4104,7 @@ static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, IN
                     NMLISTVIEW nmlv;
 
                     ZeroMemory(&nmlv, sizeof(nmlv));
-                    nmlv.iItem = lvHitTestInfo.iItem;
+                    nmlv.iItem = ht.iItem;
                     nmlv.ptAction = infoPtr->ptClickPos;
 
                     notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
@@ -4161,7 +4155,7 @@ static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
 
 /***
  * DESCRIPTION:
- * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
+ * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
  *
  * PARAMETER(S):
  * [I] infoPtr : valid pointer to the listview structure
@@ -4230,20 +4224,19 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL
     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
        uChanged |= LVIF_TEXT;
    
-    TRACE("uChanged=0x%x\n", uChanged); 
-    if (!uChanged) return TRUE;
-    *bChanged = TRUE;
+    TRACE("change mask=0x%x\n", uChanged);
     
-    ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
+    memset(&nmlv, 0, sizeof(NMLISTVIEW));
     nmlv.iItem = lpLVItem->iItem;
     nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
     nmlv.uOldState = item.state;
-    nmlv.uChanged = uChanged;
+    nmlv.uChanged = uChanged ? uChanged : lpLVItem->mask;
     nmlv.lParam = item.lParam;
-    
-    /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
-    /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
-    /* are enabled */
+
+    /* Send LVN_ITEMCHANGING notification, if the item is not being inserted
+       and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
+       are enabled. Even nothing really changed we still need to send this,
+       in this case uChanged mask is just set to passed item mask. */
     if(lpItem && !isNew && infoPtr->bDoChangeNotify)
     {
       HWND hwndSelf = infoPtr->hwndSelf;
@@ -4254,6 +4247,18 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL
        return FALSE;
     }
 
+    /* When item is inserted we need to shift existing focus index if new item has lower index. */
+    if (isNew && (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) &&
+        /* this means we won't hit a focus change path later */
+        ((uChanged & LVIF_STATE) == 0 || (!(lpLVItem->state & LVIS_FOCUSED) && (infoPtr->nFocusedItem != lpLVItem->iItem))))
+    {
+        if (infoPtr->nFocusedItem != -1 && (lpLVItem->iItem <= infoPtr->nFocusedItem))
+            infoPtr->nFocusedItem++;
+    }
+
+    if (!uChanged) return TRUE;
+    *bChanged = TRUE;
+
     /* copy information */
     if (lpLVItem->mask & LVIF_TEXT)
         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
@@ -4283,11 +4288,22 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL
        {
            ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
        }
-       /* if we are asked to change focus, and we manage it, do it */
+       /* If we are asked to change focus, and we manage it, do it.
+           It's important to have all new item data stored at this point,
+           because changing existing focus could result in a redrawing operation,
+           which in turn could ask for disp data, application should see all data
+           for inserted item when processing LVN_GETDISPINFO.
+
+           The way this works application will see nested item change notifications -
+           changed item notifications interrupted by ones from item losing focus. */
        if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
        {
            if (lpLVItem->state & LVIS_FOCUSED)
            {
+               /* update selection mark */
+               if (infoPtr->nFocusedItem == -1 && infoPtr->nSelectionMark == -1)
+                   infoPtr->nSelectionMark = lpLVItem->iItem;
+
                if (infoPtr->nFocusedItem != -1)
                {
                    /* remove current focus */
@@ -4311,7 +4327,7 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL
 
     /* if we're inserting the item, we're done */
     if (isNew) return TRUE;
-    
+
     /* send LVN_ITEMCHANGED notification */
     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
     if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
@@ -4526,104 +4542,46 @@ static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, con
     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
 }
 
-/***
- * DESCRIPTION:
- * Draws an item.
- *
- * PARAMETER(S):
- * [I] infoPtr : valid pointer to the listview structure
- * [I] hdc : device context handle
- * [I] nItem : item index
- * [I] nSubItem : subitem index
- * [I] pos : item position in client coordinates
- * [I] cdmode : custom draw mode
- *
- * RETURN:
- *   Success: TRUE
- *   Failure: FALSE
- */
-static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
+/* Draw main item or subitem */
+static void LISTVIEW_DrawItemPart(LISTVIEW_INFO *infoPtr, LVITEMW *item, const NMLVCUSTOMDRAW *nmlvcd, const POINT *pos)
 {
-    UINT uFormat;
-    WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
-    static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
-    DWORD cdsubitemmode = CDRF_DODEFAULT;
-    LPRECT lprcFocus;
-    RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
-    NMLVCUSTOMDRAW nmlvcd;
+    RECT rcSelect, rcLabel, rcBox, rcStateIcon, rcIcon;
     HIMAGELIST himl;
-    LVITEMW lvItem;
-    HFONT hOldFont;
-
-    TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
-
-    /* get information needed for drawing the item */
-    lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
-    if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
-    if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
-    lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT;
-    lvItem.iItem = nItem;
-    lvItem.iSubItem = nSubItem;
-    lvItem.state = 0;
-    lvItem.lParam = 0;
-    lvItem.cchTextMax = DISP_TEXT_SIZE;
-    lvItem.pszText = szDispText;
-    if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
-    if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
-       lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
-    if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
-    TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+    UINT format;
+    RECT *focus;
 
     /* now check if we need to update the focus rectangle */
-    lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
-
-    if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
-    LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
-    OffsetRect(&rcBox, pos.x, pos.y);
-    OffsetRect(&rcSelect, pos.x, pos.y);
-    OffsetRect(&rcIcon, pos.x, pos.y);
-    OffsetRect(&rcStateIcon, pos.x, pos.y);
-    OffsetRect(&rcLabel, pos.x, pos.y);
+    focus = infoPtr->bFocus && (item->state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
+    if (!focus) item->state &= ~LVIS_FOCUSED;
+
+    LISTVIEW_GetItemMetrics(infoPtr, item, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
+    OffsetRect(&rcBox, pos->x, pos->y);
+    OffsetRect(&rcSelect, pos->x, pos->y);
+    OffsetRect(&rcIcon, pos->x, pos->y);
+    OffsetRect(&rcStateIcon, pos->x, pos->y);
+    OffsetRect(&rcLabel, pos->x, pos->y);
     TRACE("    rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
         wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
         wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
 
-    /* fill in the custom draw structure */
-    customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
-
-    hOldFont = GetCurrentObject(hdc, OBJ_FONT);
-    if (nSubItem > 0) cdmode = infoPtr->cditemmode;
-    if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
-    if (cdmode & CDRF_NOTIFYITEMDRAW)
-        cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
-    if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
-    if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
-    /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
-    if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
-    {
-        cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
-        if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
-    }
-    if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
-        prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
-    else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
-        prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
-
-    /* in full row select, subitems, will just use main item's colors */
-    if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
-       nmlvcd.clrTextBk = CLR_NONE;
-
     /* FIXME: temporary hack */
     rcSelect.left = rcLabel.left;
 
-    /* draw the selection background, if we're drawing the main item */
-    if (nSubItem == 0)
-    {
-        /* in icon mode, the label rect is really what we want to draw the
-         * background for */
-        if (infoPtr->uView == LV_VIEW_ICON)
-           rcSelect = rcLabel;
+    /* in icon mode, the label rect is really what we want to draw the
+     * background for */
+    /* in detail mode, we want to paint background for label rect when
+     * item is not selected or listview has full row select; otherwise paint
+     * background for text only */
+    if ( infoPtr->uView == LV_VIEW_ICON ||
+        (infoPtr->uView == LV_VIEW_DETAILS && (!(item->state & LVIS_SELECTED) ||
+        (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
+        rcSelect = rcLabel;
+
+    if (nmlvcd->clrTextBk != CLR_NONE)
+        ExtTextOutW(nmlvcd->nmcd.hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
 
+    if (item->state & LVIS_FOCUSED)
+    {
        if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
        {
            /* we have to update left focus bound too if item isn't in leftmost column
@@ -4634,7 +4592,7 @@ static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nS
 
                if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
                {
-                   INT Originx = pos.x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
+                   INT Originx = pos->x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
                    INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
                                DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
 
@@ -4646,80 +4604,206 @@ static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nS
            rcSelect.right = rcBox.right;
        }
 
-       if (nmlvcd.clrTextBk != CLR_NONE)
-           ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
        /* store new focus rectangle */
-       if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
+        infoPtr->rcFocus = rcSelect;
     }
 
     /* state icons */
-    if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
+    if (infoPtr->himlState && STATEIMAGEINDEX(item->state) && (item->iSubItem == 0))
     {
-        UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
-        if (uStateImage)
+        UINT stateimage = STATEIMAGEINDEX(item->state);
+        if (stateimage)
        {
-            TRACE("uStateImage=%d\n", uStateImage);
-            ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
-                rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
+            TRACE("stateimage=%d\n", stateimage);
+            ImageList_Draw(infoPtr->himlState, stateimage-1, nmlvcd->nmcd.hdc, rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
        }
     }
 
     /* item icons */
     himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
-    if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
+    if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon))
     {
         UINT style;
 
-        TRACE("iImage=%d\n", lvItem.iImage);
+        TRACE("iImage=%d\n", item->iImage);
 
-        if (lvItem.state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
+        if (item->state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
             style = ILD_SELECTED;
         else
             style = ILD_NORMAL;
 
-        ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
+        ImageList_DrawEx(himl, item->iImage, nmlvcd->nmcd.hdc, rcIcon.left, rcIcon.top,
                          rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk,
-                         lvItem.state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
-                         style);
+                         item->state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
+                         style | (item->state & LVIS_OVERLAYMASK));
     }
 
     /* Don't bother painting item being edited */
-    if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
-   
+    if (infoPtr->hwndEdit && item->iItem == infoPtr->nEditLabelItem && item->iSubItem == 0) return;
+
     /* figure out the text drawing flags */
-    uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
+    format = (infoPtr->uView == LV_VIEW_ICON ? (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
     if (infoPtr->uView == LV_VIEW_ICON)
-       uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
-    else if (nSubItem)
+       format = (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
+    else if (item->iSubItem)
     {
-       switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
+       switch (LISTVIEW_GetColumnInfo(infoPtr, item->iSubItem)->fmt & LVCFMT_JUSTIFYMASK)
        {
-       case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
-       case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
-       default:            uFormat |= DT_LEFT;
+       case LVCFMT_RIGHT:  format |= DT_RIGHT;  break;
+       case LVCFMT_CENTER: format |= DT_CENTER; break;
+       default:            format |= DT_LEFT;
        }
     }
-    if (!(uFormat & (DT_RIGHT | DT_CENTER)))
+    if (!(format & (DT_RIGHT | DT_CENTER)))
     {
-        if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
+        if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
         else rcLabel.left += LABEL_HOR_PADDING;
     }
-    else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
+    else if (format & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
 
     /* for GRIDLINES reduce the bottom so the text formats correctly */
     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
         rcLabel.bottom--;
 
-    if (!lprcFocus && (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTSHADOWTEXT))
-        DrawShadowText(hdc, lvItem.pszText, -1, &rcLabel, uFormat, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2);
+#ifdef __REACTOS__
+    if ((!(item->state & LVIS_SELECTED) || !infoPtr->bFocus) && (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTSHADOWTEXT))
+        DrawShadowText(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2);
     else
-        DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
+#endif
+        DrawTextW(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format);
+}
+
+/***
+ * DESCRIPTION:
+ * Draws an item.
+ *
+ * PARAMETER(S):
+ * [I] infoPtr : valid pointer to the listview structure
+ * [I] hdc : device context handle
+ * [I] nItem : item index
+ * [I] nSubItem : subitem index
+ * [I] pos : item position in client coordinates
+ * [I] cdmode : custom draw mode
+ *
+ * RETURN:
+ *   Success: TRUE
+ *   Failure: FALSE
+ */
+static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERATOR *subitems, POINT pos, DWORD cdmode)
+{
+    WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
+    static WCHAR callbackW[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
+    DWORD cdsubitemmode = CDRF_DODEFAULT;
+    RECT *focus, rcBox;
+    NMLVCUSTOMDRAW nmlvcd;
+    LVITEMW lvItem;
+
+    TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc, nItem, subitems, wine_dbgstr_point(&pos));
+
+    /* get information needed for drawing the item */
+    lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
+    if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
+    lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
+    lvItem.iItem = nItem;
+    lvItem.iSubItem = 0;
+    lvItem.state = 0;
+    lvItem.lParam = 0;
+    lvItem.cchTextMax = DISP_TEXT_SIZE;
+    lvItem.pszText = szDispText;
+    if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
+    if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
+    TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+
+    /* now check if we need to update the focus rectangle */
+    focus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
+    if (!focus) lvItem.state &= ~LVIS_FOCUSED;
+
+    LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, NULL, NULL, NULL);
+    OffsetRect(&rcBox, pos.x, pos.y);
+
+    /* Full custom draw stage sequence looks like this:
+
+       LV_VIEW_DETAILS:
+
+       - CDDS_ITEMPREPAINT
+       - CDDS_ITEMPREPAINT|CDDS_SUBITEM   | => sent n times, where n is number of subitems,
+         CDDS_ITEMPOSTPAINT|CDDS_SUBITEM  |    including item iself
+       - CDDS_ITEMPOSTPAINT
+
+       other styles:
+
+       - CDDS_ITEMPREPAINT
+       - CDDS_ITEMPOSTPAINT
+    */
+
+    /* fill in the custom draw structure */
+    customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
+    if (cdmode & CDRF_NOTIFYITEMDRAW)
+        cdsubitemmode = notify_customdraw(infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
+    if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
+
+    if (subitems)
+    {
+        while (iterator_next(subitems))
+        {
+            DWORD subitemstage = CDRF_DODEFAULT;
+
+            /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
+            if (subitems->nItem)
+            {
+                lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_INDENT;
+                lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
+                lvItem.iItem = nItem;
+                lvItem.iSubItem = subitems->nItem;
+                lvItem.state = 0;
+                lvItem.lParam = 0;
+                lvItem.cchTextMax = DISP_TEXT_SIZE;
+                lvItem.pszText = szDispText;
+                if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
+                if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
+                   lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
+                if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
+                TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+
+                /* update custom draw data */
+                LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &nmlvcd.nmcd.rc, NULL, NULL, NULL, NULL);
+                OffsetRect(&nmlvcd.nmcd.rc, pos.x, pos.y);
+                nmlvcd.iSubItem = subitems->nItem;
+            }
+
+            if (cdsubitemmode & CDRF_NOTIFYSUBITEMDRAW)
+                subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
+            else
+            {
+                nmlvcd.clrTextBk = infoPtr->clrTextBk;
+                nmlvcd.clrText   = infoPtr->clrText;
+            }
+
+            if (subitems->nItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
+                prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
+            else if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
+                prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
+
+            if (!(subitemstage & CDRF_SKIPDEFAULT))
+                LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
+
+            if (subitemstage & CDRF_NOTIFYPOSTPAINT)
+                subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &nmlvcd);
+        }
+    }
+    else
+    {
+        prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
+        LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
+    }
 
 postpaint:
     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
-        notify_postpaint(infoPtr, &nmlvcd);
-    if (cdsubitemmode & CDRF_NEWFONT)
-        SelectObject(hdc, hOldFont);
+    {
+        nmlvcd.iSubItem = 0;
+        notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
+    }
+
     return TRUE;
 }
 
@@ -4846,9 +4930,15 @@ static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc,
     /* iterate through the invalidated rows */
     while(iterator_next(i))
     {
+        RANGES subitems;
+        ITERATOR k;
+
+        SelectObject(hdc, infoPtr->hFont);
        LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
        Position.y += Origin.y;
 
+        subitems = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
+
        /* iterate through the invalidated columns */
        while(iterator_next(&j))
        {
@@ -4863,8 +4953,12 @@ static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc,
                if (!RectVisible(hdc, &rcItem)) continue;
            }
 
-           LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
+            ranges_additem(subitems, j.nItem);
        }
+
+        iterator_rangesitems(&k, subitems);
+        LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, &k, Position, cdmode);
+        iterator_destroy(&k);
     }
     iterator_destroy(&j);
 }
@@ -4957,10 +5051,7 @@ static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
         itemheight =  LISTVIEW_CalculateItemHeight(infoPtr);
         rcItem.left   = infoPtr->rcList.left;
         rcItem.right  = infoPtr->rcList.right;
-        rcItem.bottom = rcItem.top = Origin.y - 1;
-        MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
-        LineTo(hdc, rcItem.right, rcItem.top);
-        for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
+        for(y = Origin.y > 1 ? Origin.y - 1 : itemheight - 1 + Origin.y % itemheight; y<=infoPtr->rcList.bottom; y+=itemheight)
         {
             rcItem.bottom = rcItem.top = y;
             TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
@@ -4971,6 +5062,8 @@ static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
         SelectObject( hdc, hOldPen );
         DeleteObject( hPen );
     }
+    else
+        ranges_destroy(colRanges);
 }
 
 /***
@@ -4994,11 +5087,12 @@ static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, D
     
     while(iterator_prev(i))
     {
+        SelectObject(hdc, infoPtr->hFont);
        LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
        Position.x += Origin.x;
        Position.y += Origin.y;
 
-        LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
+        LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, NULL, Position, cdmode);
     }
 }
 
@@ -5017,7 +5111,7 @@ static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, D
  */
 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
 {
-    COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
+    COLORREF oldTextColor = 0, oldBkColor = 0;
     NMLVCUSTOMDRAW nmlvcd;
     HFONT hOldFont = 0;
     DWORD cdmode;
@@ -5074,21 +5168,10 @@ static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcEra
                hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
     }
 
-    /* FIXME: Shouldn't need to do this */
-    oldClrTextBk = infoPtr->clrTextBk;
-    oldClrText   = infoPtr->clrText;
-   
-    infoPtr->cditemmode = CDRF_DODEFAULT;
-
     GetClientRect(infoPtr->hwndSelf, &rcClient);
     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
-    prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
-
-    /* Use these colors to draw the items */
-    infoPtr->clrTextBk = nmlvcd.clrTextBk;
-    infoPtr->clrText = nmlvcd.clrText;
 
     /* nothing to draw */
     if(infoPtr->nItemCount == 0) goto enddraw;
@@ -5141,9 +5224,6 @@ enddraw:
     if (cdmode & CDRF_NOTIFYPOSTPAINT)
        notify_postpaint(infoPtr, &nmlvcd);
 
-    infoPtr->clrTextBk = oldClrTextBk;
-    infoPtr->clrText = oldClrText;
-
     if(hbmp) {
         BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
                infoPtr->rcList.right - infoPtr->rcList.left,
@@ -5324,6 +5404,7 @@ static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LP
     POINT pos;
     HDC hdc, hdcOrig;
     HBITMAP hbmp, hOldbmp;
+    HFONT hOldFont;
     HIMAGELIST dragList = 0;
     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
 
@@ -5344,6 +5425,7 @@ static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LP
     hdc = CreateCompatibleDC(hdcOrig);
     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
     hOldbmp = SelectObject(hdc, hbmp);
+    hOldFont = SelectObject(hdc, infoPtr->hFont);
 
     rcItem.left = rcItem.top = 0;
     rcItem.right = size.cx;
@@ -5351,7 +5433,7 @@ static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LP
     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
     
     pos.x = pos.y = 0;
-    if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
+    if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, NULL, pos, CDRF_DODEFAULT))
     {
         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
         SelectObject(hdc, hOldbmp);
@@ -5360,6 +5442,7 @@ static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LP
     else
         SelectObject(hdc, hOldbmp);
 
+    SelectObject(hdc, hOldFont);
     DeleteObject(hbmp);
     DeleteDC(hdc);
     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
@@ -5383,9 +5466,8 @@ static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LP
  */
 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
 {
-    NMLISTVIEW nmlv;
     HDPA hdpaSubItems = NULL;
-    BOOL bSuppress;
+    BOOL suppress = FALSE;
     ITEMHDR *hdrItem;
     ITEM_INFO *lpItem;
     ITEM_ID *lpID;
@@ -5400,11 +5482,15 @@ static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
     SetRectEmpty(&infoPtr->rcFocus);
     /* But we are supposed to leave nHotItem as is! */
 
-
     /* send LVN_DELETEALLITEMS notification */
-    ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
-    nmlv.iItem = -1;
-    bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
+    if (!(infoPtr->dwStyle & LVS_OWNERDATA) || !destroy)
+    {
+        NMLISTVIEW nmlv;
+
+        memset(&nmlv, 0, sizeof(NMLISTVIEW));
+        nmlv.iItem = -1;
+        suppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
+    }
 
     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
     {
@@ -5412,7 +5498,7 @@ static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
        {
            /* send LVN_DELETEITEM notification, if not suppressed
               and if it is not a virtual listview */
-           if (!bSuppress) notify_deleteitem(infoPtr, i);
+           if (!suppress) notify_deleteitem(infoPtr, i);
            hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
            lpItem = DPA_GetPtr(hdpaSubItems, 0);
            /* free id struct */
@@ -5644,9 +5730,8 @@ static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
     {
-       TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
-       ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
-                      &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
+       TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
+       InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
     }
 
     /* report has only that column, so we're done */
@@ -5659,8 +5744,7 @@ static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
     OffsetRect(&rcScroll, Origin.x, Origin.y);
     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
-       ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
-                      &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
+       InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
 }
 
 /***
@@ -5679,6 +5763,7 @@ static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
 {
     LVITEMW item;
     const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
+    INT focus = infoPtr->nFocusedItem;
 
     TRACE("(nItem=%d)\n", nItem);
 
@@ -5688,7 +5773,7 @@ static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
     item.state = 0;
     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
     LISTVIEW_SetItemState(infoPtr, nItem, &item);
-           
+
     /* send LVN_DELETEITEM notification. */
     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
 
@@ -5729,6 +5814,7 @@ static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
 
     infoPtr->nItemCount--;
     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
+    LISTVIEW_ShiftFocus(infoPtr, focus, nItem, -1);
 
     /* now is the invalidation fun */
     if (!is_icon)
@@ -6258,6 +6344,7 @@ again:
     {
         lvItem.iItem = nItem;
         lvItem.iSubItem = 0;
+        lvItem.pszText = szDispText;
         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
 
        if (lvItem.mask & LVIF_PARAM)
@@ -6556,7 +6643,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     HDPA hdpaSubItems;
     INT isubitem;
 
-    TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
+    TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
 
     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
        return FALSE;
@@ -7047,65 +7134,61 @@ static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT
  * 
  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
  *       not only those of the first column.
- *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
  * 
  * RETURN:
  *     TRUE: success
  *     FALSE: failure
  */
-static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
+static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT item, LPRECT lprc)
 {
-    POINT Position, Origin;
-    LVITEMW lvItem;
-    INT nColumn;
+    RECT rect = { 0, 0, 0, 0 };
+    POINT origin;
+    INT y;
     
     if (!lprc) return FALSE;
 
-    nColumn = lprc->top;
-
-    TRACE("(nItem=%d, nSubItem=%d, type=%d)\n", nItem, lprc->top, lprc->left);
-    /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
+    TRACE("(item=%d, subitem=%d, type=%d)\n", item, lprc->top, lprc->left);
+    /* Subitem of '0' means item itself, and this works for all control view modes */
     if (lprc->top == 0)
-        return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
+        return LISTVIEW_GetItemRect(infoPtr, item, lprc);
 
     if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
 
-    /* special case for header items */
-    if (nItem == -1)
-    {
-        if (lprc->left != LVIR_BOUNDS)
-        {
-            FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
-            return FALSE;
-        }
+    LISTVIEW_GetOrigin(infoPtr, &origin);
+    /* this works for any item index, no matter if it exists or not */
+    y = item * infoPtr->nItemHeight + origin.y;
 
-        if (infoPtr->hwndHeader)
-            return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
-        else
-        {
-            memset(lprc, 0, sizeof(RECT));
-            return TRUE;
-        }
+    if (infoPtr->hwndHeader && SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)&rect))
+    {
+        rect.top = 0;
+        rect.bottom = infoPtr->nItemHeight;
+    }
+    else
+    {
+        /* Native implementation is broken for this case and garbage is left for left and right fields,
+           we zero them to get predictable output */
+        lprc->left = lprc->right = lprc->top = 0;
+        lprc->bottom = infoPtr->nItemHeight;
+        OffsetRect(lprc, origin.x, y);
+        TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
+        return TRUE;
     }
 
-    if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
-    LISTVIEW_GetOrigin(infoPtr, &Origin);
-
-    if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
-
-    lvItem.mask = 0;
-    lvItem.iItem = nItem;
-    lvItem.iSubItem = nColumn;
-    
-    switch(lprc->left)
+    switch (lprc->left)
     {
     case LVIR_ICON:
-       LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
-        break;
+    {
+        /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
+        if (infoPtr->himlSmall)
+            rect.right = rect.left + infoPtr->iconSize.cx;
+        else
+            rect.right = rect.left;
 
+        rect.bottom = rect.top + infoPtr->iconSize.cy;
+        break;
+    }
     case LVIR_LABEL:
     case LVIR_BOUNDS:
-       LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
         break;
 
     default:
@@ -7113,7 +7196,8 @@ static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPR
        return FALSE;
     }
 
-    OffsetRect(lprc, Origin.x, Position.y);
+    OffsetRect(&rect, origin.x, y);
+    *lprc = rect;
     TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
 
     return TRUE;
@@ -7502,6 +7586,7 @@ static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht,
     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
     POINT Origin, Position, opt;
+    BOOL is_fullrow;
     LVITEMW lvItem;
     ITERATOR i;
     INT iItem;
@@ -7625,15 +7710,17 @@ static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht,
     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
     if (!PtInRect(&rcBounds, opt)) return -1;
 
+    /* That's a special case - row rectangle is used as item rectangle and
+       returned flags contain all item parts. */
+    is_fullrow = (infoPtr->uView == LV_VIEW_DETAILS) && ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || (infoPtr->dwStyle & LVS_OWNERDRAWFIXED));
+
     if (PtInRect(&rcIcon, opt))
        lpht->flags |= LVHT_ONITEMICON;
     else if (PtInRect(&rcLabel, opt))
        lpht->flags |= LVHT_ONITEMLABEL;
     else if (infoPtr->himlState && PtInRect(&rcState, opt))
        lpht->flags |= LVHT_ONITEMSTATEICON;
-    /* special case for LVS_EX_FULLROWSELECT */
-    if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
-      !(lpht->flags & LVHT_ONITEM))
+    if (is_fullrow && !(lpht->flags & LVHT_ONITEM))
     {
        lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
     }
@@ -7641,9 +7728,7 @@ static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht,
        lpht->flags &= ~LVHT_NOWHERE;
     TRACE("lpht->flags=0x%x\n", lpht->flags); 
 
-    if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
-                    ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
-                     (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
+    if (select && !is_fullrow)
     {
         if (infoPtr->uView == LV_VIEW_DETAILS)
         {
@@ -7682,7 +7767,7 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
     LVITEMW item;
     HWND hwndSelf = infoPtr->hwndSelf;
 
-    TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
+    TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
 
     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
 
@@ -7732,7 +7817,7 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
     else
         nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
 
-    TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
+    TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
     if (nItem == -1) goto fail;
     infoPtr->nItemCount++;
@@ -7764,6 +7849,7 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
         item.state &= ~LVIS_STATEIMAGEMASK;
         item.state |= INDEXTOSTATEIMAGEMASK(1);
     }
+
     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
 
     /* make room for the position, if we are in the right mode */
@@ -7777,9 +7863,9 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
            goto undo;
        }
     }
-    
+
     /* send LVN_INSERTITEM notification */
-    ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
+    memset(&nmlv, 0, sizeof(NMLISTVIEW));
     nmlv.iItem = nItem;
     nmlv.lParam = lpItem->lParam;
     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
@@ -7805,6 +7891,7 @@ static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
 
 undo:
     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
+    LISTVIEW_ShiftFocus(infoPtr, infoPtr->nFocusedItem, nItem, -1);
     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
     infoPtr->nItemCount--;
 fail:
@@ -7898,17 +7985,6 @@ static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT n
  *  nearest number of pixels that are a whole line. Ex: if line height
  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
  *  is passed, then the scroll will be 0.  (per MSDN 7/2002)
- *
- *  For:  (per experimentation with native control and CSpy ListView)
- *     LV_VIEW_ICON       scrolling in any direction is allowed
- *     LV_VIEW_SMALLICON  scrolling in any direction is allowed
- *     LV_VIEW_LIST       dx=1 = 1 column (horizontal only)
- *                           but will only scroll 1 column per message
- *                           no matter what the value.
- *                        dy must be 0 or FALSE returned.
- *     LV_VIEW_DETAILS    dx=1 = 1 pixel
- *                        dy=  see above
- *
  */
 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
 {
@@ -8258,7 +8334,7 @@ static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
     INT max_cx = 0;
     HDITEMW hdi;
 
-    TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
+    TRACE("(nColumn=%d, cx=%d)\n", nColumn, cx);
 
     /* set column width only if in report or list mode */
     if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
@@ -8285,10 +8361,10 @@ static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
        lvItem.mask = LVIF_TEXT;        
        lvItem.iItem = 0;
        lvItem.iSubItem = nColumn;
-       lvItem.pszText = szDispText;
        lvItem.cchTextMax = DISP_TEXT_SIZE;
        for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
        {
+            lvItem.pszText = szDispText;
            if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
            nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
            if (max_cx < nLabelWidth) max_cx = nLabelWidth;
@@ -8572,34 +8648,33 @@ static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
  */
 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
 {
+    INT iconWidth = 0, iconHeight = 0;
     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
 
     TRACE("requested=(%d,%d)\n", cx, cy);
-    
-    /* this is supported only for LVS_ICON style */
-    if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
-  
+
     /* set to defaults, if instructed to */
-    if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
-    if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
-
-    /* if 0 then compute width
-     * FIXME: Should scan each item and determine max width of
-     *        icon or label, then make that the width */
-    if (cx == 0)
-       cx = infoPtr->iconSpacing.cx;
-
-    /* if 0 then compute height */
-    if (cy == 0) 
-       cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
-            ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
-    
+    if (cx == -1 && cy == -1)
+    {
+        infoPtr->autoSpacing = TRUE;
+        if (infoPtr->himlNormal)
+            ImageList_GetIconSize(infoPtr->himlNormal, &iconWidth, &iconHeight);
+        cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON) + iconWidth;
+        cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON) + iconHeight;
+    }
+    else
+        infoPtr->autoSpacing = FALSE;
+
+    /* if 0 then keep width */
+    if (cx != 0)
+        infoPtr->iconSpacing.cx = cx;
 
-    infoPtr->iconSpacing.cx = cx;
-    infoPtr->iconSpacing.cy = cy;
+    /* if 0 then keep height */
+    if (cy != 0)
+        infoPtr->iconSpacing.cy = cy;
 
     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
-         LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
+          LOWORD(oldspacing), HIWORD(oldspacing), infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
          infoPtr->iconSize.cx, infoPtr->iconSize.cy,
          infoPtr->ntmHeight);
 
@@ -8609,7 +8684,7 @@ static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
     return oldspacing;
 }
 
-static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
+static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL is_small)
 {
     INT cx, cy;
     
@@ -8620,8 +8695,8 @@ static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
     }
     else
     {
-       size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
-       size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
+       size->cx = GetSystemMetrics(is_small ? SM_CXSMICON : SM_CXICON);
+       size->cy = GetSystemMetrics(is_small ? SM_CYSMICON : SM_CYICON);
     }
 }
 
@@ -8643,7 +8718,7 @@ static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAG
     INT oldHeight = infoPtr->nItemHeight;
     HIMAGELIST himlOld = 0;
 
-    TRACE("(nType=%d, himl=%p\n", nType, himl);
+    TRACE("(nType=%d, himl=%p)\n", nType, himl);
 
     switch (nType)
     {
@@ -8651,13 +8726,16 @@ static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAG
         himlOld = infoPtr->himlNormal;
         infoPtr->himlNormal = himl;
         if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
-        LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
+        if (infoPtr->autoSpacing)
+            LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
     break;
 
     case LVSIL_SMALL:
         himlOld = infoPtr->himlSmall;
         infoPtr->himlSmall = himl;
         if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
+        if (infoPtr->hwndHeader)
+            SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)himl);
     break;
 
     case LVSIL_STATE:
@@ -8795,7 +8873,7 @@ static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, const PO
 {
     POINT Origin, Pt;
 
-    TRACE("(nItem=%d, pt=%s\n", nItem, wine_dbgstr_point(pt));
+    TRACE("(nItem=%d, pt=%s)\n", nItem, wine_dbgstr_point(pt));
 
     if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
        !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
@@ -8829,26 +8907,35 @@ static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, const PO
  * PARAMETER(S):
  * [I] infoPtr : valid pointer to the listview structure
  * [I] nItem : item index
- * [I] lpLVItem : item or subitem info
+ * [I] item  : item or subitem info
  *
  * RETURN:
  *   SUCCESS : TRUE
  *   FAILURE : FALSE
  */
-static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
+static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *item)
 {
-    BOOL bResult = TRUE;
+    BOOL ret = TRUE;
     LVITEMW lvItem;
 
+    if (!item) return FALSE;
+
     lvItem.iItem = nItem;
     lvItem.iSubItem = 0;
     lvItem.mask = LVIF_STATE;
-    lvItem.state = lpLVItem->state;
-    lvItem.stateMask = lpLVItem->stateMask;
-    TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+    lvItem.state = item->state;
+    lvItem.stateMask = item->stateMask;
+    TRACE("item=%s\n", debuglvitem_t(&lvItem, TRUE));
 
     if (nItem == -1)
     {
+        UINT oldstate = 0;
+        BOOL notify;
+
+        /* special case optimization for recurring attempt to deselect all */
+        if (lvItem.state == 0 && lvItem.stateMask == LVIS_SELECTED && !LISTVIEW_GetSelectedCount(infoPtr))
+            return TRUE;
+
        /* select all isn't allowed in LVS_SINGLESEL */
        if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
            return FALSE;
@@ -8856,14 +8943,40 @@ static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITE
        /* focus all isn't allowed */
        if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
 
+        notify = infoPtr->bDoChangeNotify;
+        if (infoPtr->dwStyle & LVS_OWNERDATA)
+        {
+            infoPtr->bDoChangeNotify = FALSE;
+            if (!(lvItem.state & LVIS_SELECTED) && LISTVIEW_GetSelectedCount(infoPtr))
+                oldstate |= LVIS_SELECTED;
+            if (infoPtr->nFocusedItem != -1) oldstate |= LVIS_FOCUSED;
+        }
+
        /* apply to all items */
        for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
-           if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
+           if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) ret = FALSE;
+
+        if (infoPtr->dwStyle & LVS_OWNERDATA)
+        {
+            NMLISTVIEW nmlv;
+
+            infoPtr->bDoChangeNotify = notify;
+
+            nmlv.iItem = -1;
+            nmlv.iSubItem = 0;
+            nmlv.uNewState = lvItem.state & lvItem.stateMask;
+            nmlv.uOldState = oldstate & lvItem.stateMask;
+            nmlv.uChanged = LVIF_STATE;
+            nmlv.ptAction.x = nmlv.ptAction.y = 0;
+            nmlv.lParam = 0;
+
+            notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
+        }
     }
     else
-       bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
+       ret = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
 
-    return bResult;
+    return ret;
 }
 
 /***
@@ -8885,6 +8998,7 @@ static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITE
     LVITEMW lvItem;
 
     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
+    if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
 
     lvItem.iItem = nItem;
     lvItem.iSubItem = lpLVItem->iSubItem;
@@ -9007,7 +9121,6 @@ static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL unicode)
  */
 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
 {
-  SIZE oldIconSize = infoPtr->iconSize;
   HIMAGELIST himl;
 
   if (infoPtr->uView == nView) return 1;
@@ -9033,14 +9146,6 @@ static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
   switch (nView)
   {
   case LV_VIEW_ICON:
-      if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
-      {
-            TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
-                   oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
-           LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
-      }
-      LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
-      break;
   case LV_VIEW_SMALLICON:
       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
       break;
@@ -9252,21 +9357,15 @@ static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
  */
 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
 {
-    HPEN hOldPen;
     HDC hdc;
-    INT oldROP;
 
     if (infoPtr->xTrackLine == -1)
         return FALSE;
 
     if (!(hdc = GetDC(infoPtr->hwndSelf)))
         return FALSE;
-    hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
-    oldROP = SetROP2(hdc, R2_XORPEN);
-    MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
-    LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
-    SetROP2(hdc, oldROP);
-    SelectObject(hdc, hOldPen);
+    PatBlt( hdc, infoPtr->xTrackLine, infoPtr->rcList.top,
+            1, infoPtr->rcList.bottom - infoPtr->rcList.top, DSTINVERT );
     ReleaseDC(infoPtr->hwndSelf, hdc);
     return TRUE;
 }
@@ -9343,11 +9442,13 @@ static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
   infoPtr->bRedraw = TRUE;
   infoPtr->bNoItemMetrics = TRUE;
   infoPtr->bDoChangeNotify = TRUE;
-  infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
-  infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
+  infoPtr->autoSpacing = TRUE;
+  infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON);
+  infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
   infoPtr->nEditLabelItem = -1;
   infoPtr->nLButtonDownItem = -1;
   infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */
+  infoPtr->cWheelRemainder = 0;
   infoPtr->nMeasureItemHeight = 0;
   infoPtr->xTrackLine = -1;  /* no track line */
   infoPtr->itemEdit.fEnabled = FALSE;
@@ -9418,6 +9519,7 @@ static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
 
   /* init item size to avoid division by 0 */
   LISTVIEW_UpdateItemSize (infoPtr);
+  LISTVIEW_UpdateSize (infoPtr);
 
   if (infoPtr->uView == LV_VIEW_DETAILS)
   {
@@ -9747,14 +9849,10 @@ static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
 
 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
 {
-    INT gcWheelDelta = 0;
-    INT pulScrollLines = 3;
+    UINT pulScrollLines = 3;
 
     TRACE("(wheelDelta=%d)\n", wheelDelta);
 
-    SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
-    gcWheelDelta -= wheelDelta;
-
     switch(infoPtr->uView)
     {
     case LV_VIEW_ICON:
@@ -9763,21 +9861,31 @@ static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
         *  should be fixed in the future.
         */
-        LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
+        LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (wheelDelta > 0) ?
                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE);
         break;
 
     case LV_VIEW_DETAILS:
-        if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
+        SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
+
+        /* if scrolling changes direction, ignore left overs */
+        if ((wheelDelta < 0 && infoPtr->cWheelRemainder < 0) ||
+            (wheelDelta > 0 && infoPtr->cWheelRemainder > 0))
+            infoPtr->cWheelRemainder += wheelDelta;
+        else
+            infoPtr->cWheelRemainder = wheelDelta;
+        if (infoPtr->cWheelRemainder && pulScrollLines)
         {
-            int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
-            cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
-            LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll);
+            int cLineScroll;
+            pulScrollLines = min((UINT)LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
+            cLineScroll = pulScrollLines * (float)infoPtr->cWheelRemainder / WHEEL_DELTA;
+            infoPtr->cWheelRemainder -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines;
+            LISTVIEW_VScroll(infoPtr, SB_INTERNAL, -cLineScroll);
         }
         break;
 
     case LV_VIEW_LIST:
-        LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0);
+        LISTVIEW_HScroll(infoPtr, (wheelDelta > 0) ? SB_LINELEFT : SB_LINERIGHT, 0);
         break;
     }
     return 0;
@@ -9904,7 +10012,10 @@ static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
 {
     TRACE("()\n");
 
-    /* if we did not have the focus, there's nothing to do */
+    /* drop any left over scroll amount */
+    infoPtr->cWheelRemainder = 0;
+
+    /* if we did not have the focus, there's nothing more to do */
     if (!infoPtr->bFocus) return 0;
    
     /* send NM_KILLFOCUS notification */
@@ -9977,6 +10088,52 @@ static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x,
     return 0;
 }
 
+static LRESULT LISTVIEW_TrackMouse(const LISTVIEW_INFO *infoPtr, POINT pt)
+{
+    MSG msg;
+    RECT r;
+
+    r.top = r.bottom = pt.y;
+    r.left = r.right = pt.x;
+
+    InflateRect(&r, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
+
+    SetCapture(infoPtr->hwndSelf);
+
+    while (1)
+    {
+       if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
+       {
+           if (msg.message == WM_MOUSEMOVE)
+           {
+               pt.x = (short)LOWORD(msg.lParam);
+               pt.y = (short)HIWORD(msg.lParam);
+               if (PtInRect(&r, pt))
+                   continue;
+               else
+               {
+                   ReleaseCapture();
+                   return 1;
+               }
+           }
+           else if (msg.message >= WM_LBUTTONDOWN &&
+                    msg.message <= WM_RBUTTONDBLCLK)
+           {
+               break;
+           }
+
+           DispatchMessageW(&msg);
+       }
+
+       if (GetCapture() != infoPtr->hwndSelf)
+           return 0;
+    }
+
+    ReleaseCapture();
+    return 0;
+}
+
+
 /***
  * DESCRIPTION:
  * Processes mouse down messages (left mouse button).
@@ -10230,9 +10387,9 @@ static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
  * RETURN:
  * Zero
  */
-static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr)
+static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, NMHDR *lpnmhdr)
 {
-    const NMHEADERW *lpnmh;
+    NMHEADERW *lpnmh;
     
     TRACE("(lpnmhdr=%p)\n", lpnmhdr);
 
@@ -10240,7 +10397,7 @@ static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr)
 
     /* remember: HDN_LAST < HDN_FIRST */
     if (lpnmhdr->code > HDN_FIRST || lpnmhdr->code < HDN_LAST) return 0;
-    lpnmh = (const NMHEADERW *)lpnmhdr;
+    lpnmh = (NMHEADERW *)lpnmhdr;
 
     if (lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
 
@@ -10265,26 +10422,25 @@ static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr)
             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
             infoPtr->xTrackLine = x + ptOrigin.x;
             LISTVIEW_DrawTrackLine(infoPtr);
-            break;
+            return notify_forward_header(infoPtr, lpnmh);
        }
-       
+
        case HDN_ENDTRACKA:
        case HDN_ENDTRACKW:
            /* remove the track line (if any) */
            LISTVIEW_DrawTrackLine(infoPtr);
            infoPtr->xTrackLine = -1;
-           break;
+            return notify_forward_header(infoPtr, lpnmh);
 
         case HDN_BEGINDRAG:
-            notify_forward_header(infoPtr, lpnmh);
-            return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
+            if ((infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0) return 1;
+            return notify_forward_header(infoPtr, lpnmh);
 
         case HDN_ENDDRAG:
             infoPtr->colRectsDirty = TRUE;
             LISTVIEW_InvalidateList(infoPtr);
-            notify_forward_header(infoPtr, lpnmh);
-            return FALSE;
-            
+            return notify_forward_header(infoPtr, lpnmh);
+
        case HDN_ITEMCHANGEDW:
        case HDN_ITEMCHANGEDA:
        {
@@ -10364,8 +10520,8 @@ static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr)
                    LISTVIEW_InvalidateRect(infoPtr, &rcCol);
                }
            }
-       }
-       break;
+           break;
+        }
 
        case HDN_ITEMCLICKW:
        case HDN_ITEMCLICKA:
@@ -10377,9 +10533,8 @@ static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr)
             nmlv.iItem = -1;
             nmlv.iSubItem = lpnmh->iItem;
             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
-            notify_forward_header(infoPtr, lpnmh);
+            return notify_forward_header(infoPtr, lpnmh);
         }
-       break;
 
        case HDN_DIVIDERDBLCLICKW:
        case HDN_DIVIDERDBLCLICKA:
@@ -10387,10 +10542,8 @@ static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr)
                       we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
                       split needed for that */
             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
-            notify_forward_header(infoPtr, lpnmh);
-            break;
+            return notify_forward_header(infoPtr, lpnmh);
     }
-
     return 0;
 }
 
@@ -10585,94 +10738,76 @@ static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, I
 
 /***
  * DESCRIPTION:
- * Processes mouse down messages (right mouse button).
+ * Processes WM_RBUTTONDOWN message and corresponding drag operation.
  *
  * PARAMETER(S):
  * [I] infoPtr : valid pointer to the listview structure
  * [I] wKey : key flag
- * [I] x,y : mouse coordinate
+ * [I] x, y : mouse coordinate
  *
  * RETURN:
  * Zero
  */
 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
 {
-    LVHITTESTINFO lvHitTestInfo;
-    INT nItem;
+    LVHITTESTINFO ht;
+    INT item;
 
-    TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y);
+    TRACE("(key=%hu, x=%d, y=%d)\n", wKey, x, y);
 
     /* send NM_RELEASEDCAPTURE notification */
     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
 
+    /* determine the index of the selected item */
+    ht.pt.x = x;
+    ht.pt.y = y;
+    item = LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
+
     /* make sure the listview control window has the focus */
     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
 
-    /* set right button down flag */
-    infoPtr->bRButtonDown = TRUE;
-
-    /* determine the index of the selected item */
-    lvHitTestInfo.pt.x = x;
-    lvHitTestInfo.pt.y = y;
-    nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
-  
-    if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
+    if ((item >= 0) && (item < infoPtr->nItemCount))
     {
-       LISTVIEW_SetItemFocus(infoPtr, nItem);
+       LISTVIEW_SetItemFocus(infoPtr, item);
        if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
-            !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
-           LISTVIEW_SetSelection(infoPtr, nItem);
+            !LISTVIEW_GetItemState(infoPtr, item, LVIS_SELECTED))
+           LISTVIEW_SetSelection(infoPtr, item);
     }
     else
-    {
        LISTVIEW_DeselectAll(infoPtr);
-    }
-
-    return 0;
-}
 
-/***
- * DESCRIPTION:
- * Processes mouse up messages (right mouse button).
- *
- * PARAMETER(S):
- * [I] infoPtr : valid pointer to the listview structure
- * [I] wKey : key flag
- * [I] x,y : mouse coordinate
- *
- * RETURN:
- * Zero
- */
-static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
-{
-    LVHITTESTINFO lvHitTestInfo;
-    POINT pt;
-
-    TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y);
+    if (LISTVIEW_TrackMouse(infoPtr, ht.pt))
+    {
+       if (ht.iItem != -1)
+       {
+            NMLISTVIEW nmlv;
 
-    if (!infoPtr->bRButtonDown) return 0;
-    /* set button flag */
-    infoPtr->bRButtonDown = FALSE;
+            memset(&nmlv, 0, sizeof(nmlv));
+            nmlv.iItem = ht.iItem;
+            nmlv.ptAction = ht.pt;
 
-    /* Send NM_RCLICK notification */
-    lvHitTestInfo.pt.x = x;
-    lvHitTestInfo.pt.y = y;
-    LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
-    if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
+            notify_listview(infoPtr, LVN_BEGINRDRAG, &nmlv);
+       }
+    }
+    else
+    {
+       SetFocus(infoPtr->hwndSelf);
 
-    /* Change to screen coordinate for WM_CONTEXTMENU */
-    pt = lvHitTestInfo.pt;
-    ClientToScreen(infoPtr->hwndSelf, &pt);
+        ht.pt.x = x;
+        ht.pt.y = y;
+        LISTVIEW_HitTest(infoPtr, &ht, TRUE, FALSE);
 
-    /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
-    SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
-                (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
+       if (notify_click(infoPtr, NM_RCLICK, &ht))
+       {
+           /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
+           SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
+               (WPARAM)infoPtr->hwndSelf, (LPARAM)GetMessagePos());
+       }
+    }
 
     return 0;
 }
 
-
 /***
  * DESCRIPTION:
  * Sets the cursor.
@@ -10755,6 +10890,7 @@ static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
 {
     HFONT oldFont = infoPtr->hFont;
+    INT oldHeight = infoPtr->nItemHeight;
 
     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
 
@@ -10763,12 +10899,16 @@ static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedra
     
     LISTVIEW_SaveTextMetrics(infoPtr);
 
+    infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
+
     if (infoPtr->uView == LV_VIEW_DETAILS)
     {
        SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
         LISTVIEW_UpdateSize(infoPtr);
         LISTVIEW_UpdateScroll(infoPtr);
     }
+    else if (infoPtr->nItemHeight != oldHeight)
+        LISTVIEW_UpdateScroll(infoPtr);
 
     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
 
@@ -10873,7 +11013,7 @@ static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
         * The "2" is there to mimic the native control. I think it may be
         * related to either padding or edges.  (GLA 7/2002)
         */
-       if (!(infoPtr->dwStyle & WS_HSCROLL))
+       if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
            infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
     }
@@ -10946,7 +11086,6 @@ static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
 
     if (uNewView != uOldView)
     {
-       SIZE oldIconSize = infoPtr->iconSize;
        HIMAGELIST himl;
     
         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
@@ -10957,17 +11096,8 @@ static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
 
         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
-    
-        if (uNewView == LVS_ICON)
-        {
-            if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
-            {
-                TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
-                     oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
-               LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
-            }
-        }
-        else if (uNewView == LVS_REPORT)
+
+        if (uNewView == LVS_REPORT)
         {
             HDLAYOUT hl;
             WINDOWPOS wp;
@@ -11398,7 +11528,9 @@ LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam);
 
   case LVM_SETICONSPACING:
-    return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
+    if(lParam == -1)
+        return LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
+    return LISTVIEW_SetIconSpacing(infoPtr, LOWORD(lParam), HIWORD(lParam));
 
   case LVM_SETIMAGELIST:
     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
@@ -11431,7 +11563,6 @@ LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
 
   case LVM_SETITEMSTATE:
-    if (lParam == 0) return FALSE;
     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
 
   case LVM_SETITEMTEXTA:
@@ -11562,9 +11693,6 @@ LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
   case WM_RBUTTONDOWN:
     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
 
-  case WM_RBUTTONUP:
-    return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
-
   case WM_SETCURSOR:
     return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
 
@@ -11580,9 +11708,6 @@ LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
   case WM_SHOWWINDOW:
     return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
 
-  case WM_SIZE:
-    return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
-
   case WM_STYLECHANGED:
     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
 
@@ -11614,16 +11739,14 @@ LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
   case WM_WINDOWPOSCHANGED:
       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
       {
-      SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
-                  SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
-
-      if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
-      {
-          if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
-      }
+          SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
+                       SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
 
-         LISTVIEW_UpdateSize(infoPtr);
-         LISTVIEW_UpdateScroll(infoPtr);
+          if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
+          {
+              if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
+          }
+          LISTVIEW_Size(infoPtr, ((WINDOWPOS *)lParam)->cx, ((WINDOWPOS *)lParam)->cy);
       }
       return DefWindowProcW(hwnd, uMsg, wParam, lParam);