diff dwt/widgets/Table.d @ 213:36f5cb12e1a2

Update to SWT 3.4M7
author Frank Benoit <benoit@tionex.de>
date Sat, 17 May 2008 17:34:28 +0200
parents ab60f3309436
children a8fed3e56433
line wrap: on
line diff
--- a/dwt/widgets/Table.d	Mon May 05 00:12:38 2008 +0200
+++ b/dwt/widgets/Table.d	Sat May 17 17:34:28 2008 +0200
@@ -33,6 +33,7 @@
 import dwt.widgets.Event;
 import dwt.widgets.Control;
 import dwt.widgets.Display;
+import dwt.widgets.Shell;
 
 import dwt.dwthelper.utils;
 
@@ -66,12 +67,13 @@
  * </pre></code>
  * </p><p>
  * Note that although this class is a subclass of <code>Composite</code>,
- * it does not make sense to add <code>Control</code> children to it,
- * or set a layout on it.
+ * it does not normally make sense to add <code>Control</code> children to
+ * it, or set a layout on it, unless implementing something like a cell
+ * editor.
  * </p><p>
  * <dl>
  * <dt><b>Styles:</b></dt>
- * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL</dd>
+ * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL, NO_SCROLL</dd>
  * <dt><b>Events:</b></dt>
  * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd>
  * </dl>
@@ -96,10 +98,11 @@
     ImageList imageList, headerImageList;
     TableItem currentItem;
     TableColumn sortColumn;
+    RECT* focusRect;
+    HWND headerToolTipHandle;
     bool ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus, ignoreDrawSelection, ignoreDrawHot;
     bool customDraw, dragStarted, explorerTheme, firstColumnImage, fixScrollWidth, tipRequested, wasSelected, wasResized;
-    bool ignoreActivate, ignoreSelect, ignoreShrink, ignoreResize, ignoreColumnMove, ignoreColumnResize;
-    HWND headerToolTipHandle;
+    bool ignoreActivate, ignoreSelect, ignoreShrink, ignoreResize, ignoreColumnMove, ignoreColumnResize, fullRowSelect;
     int itemHeight, lastIndexOf, lastWidth, sortDirection, resizeCount, selectionForeground, hotIndex;
     static /*final*/ WNDPROC HeaderProc;
     static const int INSET = 4;
@@ -109,6 +112,8 @@
     static const int HEADER_EXTRA = 3;
     static const int VISTA_EXTRA = 2;
     static const int EXPLORER_EXTRA = 2;
+    static final int H_SCROLL_LIMIT = 32;
+    static final int V_SCROLL_LIMIT = 16;
     static const bool EXPLORER_THEME = true;
     private static /+const+/ WNDPROC TableProc;
     static const TCHAR[] TableClass = OS.WC_LISTVIEW;
@@ -159,6 +164,7 @@
  * @see DWT#FULL_SELECTION
  * @see DWT#HIDE_SELECTION
  * @see DWT#VIRTUAL
+ * @see DWT#NO_SCROLL
  * @see Widget#checkSubclass
  * @see Widget#getStyle
  */
@@ -176,8 +182,7 @@
             setCustomDraw (true);
             setBackgroundTransparent (true);
             if (OS.COMCTL32_MAJOR < 6) style |= DWT.DOUBLE_BUFFERED;
-            //TODO - LVS_EX_LABELTIP causes white rectangles (turn it off)
-            OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_LABELTIP, 0);
+            if (OS.IsWinCE) OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_LABELTIP, 0);
             break;
         default:
     }
@@ -263,7 +268,16 @@
         /* Resize messages */
         case OS.WM_WINDOWPOSCHANGED:
             redraw = findImageControl () !is null && drawCount is 0 && OS.IsWindowVisible (handle);
-            if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+            if (redraw) {
+                /*
+                * Feature in Windows.  When LVM_SETBKCOLOR is used with CLR_NONE
+                * to make the background of the table transparent, drawing becomes
+                * slow.  The fix is to temporarily clear CLR_NONE when redraw is
+                * turned off.
+                */
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+                OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
+            }
             //FALL THROUGH
 
         /* Mouse messages */
@@ -298,7 +312,66 @@
     bool oldSelected = wasSelected;
     if (checkSelection) wasSelected = false;
     if (checkActivate) ignoreActivate = true;
-    int code = OS.CallWindowProc (TableProc, hwnd, msg, wParam, lParam);
+
+    /*
+    * Bug in Windows.  For some reason, when the WS_EX_COMPOSITED
+    * style is set in a parent of a table and the header is visible,
+    * Windows issues an endless stream of WM_PAINT messages.  The
+    * fix is to call BeginPaint() and EndPaint() outside of WM_PAINT
+    * and pass the paint HDC in to the window proc.
+    */
+    bool fixPaint = false;
+    if (msg is OS.WM_PAINT) {
+        int bits0 = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits0 & OS.LVS_NOCOLUMNHEADER) is 0) {
+            HWND hwndParent = OS.GetParent (handle), hwndOwner = null;
+            while (hwndParent !is null) {
+                int bits1 = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
+                if ((bits1 & OS.WS_EX_COMPOSITED) !is 0) {
+                    fixPaint = true;
+                    break;
+                }
+                hwndOwner = OS.GetWindow (hwndParent, OS.GW_OWNER);
+                if (hwndOwner !is null) break;
+                hwndParent = OS.GetParent (hwndParent);
+            }
+        }
+    }
+
+    /* Remove the scroll bars that Windows keeps automatically adding */
+    bool fixScroll = false;
+    if ((style & DWT.H_SCROLL) is 0 || (style & DWT.V_SCROLL) is 0) {
+        switch (msg) {
+            case OS.WM_PAINT:
+            case OS.WM_NCPAINT:
+            case OS.WM_WINDOWPOSCHANGING: {
+                int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
+                if ((style & DWT.H_SCROLL) is 0 && (bits & OS.WS_HSCROLL) !is 0) {
+                    fixScroll = true;
+                    bits &= ~OS.WS_HSCROLL;
+                }
+                if ((style & DWT.V_SCROLL) is 0 && (bits & OS.WS_VSCROLL) !is 0) {
+                    fixScroll = true;
+                    bits &= ~OS.WS_VSCROLL;
+                }
+                if (fixScroll) OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+            }
+        }
+    }
+    int /*long*/ code = 0;
+    if (fixPaint) {
+        PAINTSTRUCT ps;
+        auto hDC = OS.BeginPaint (hwnd, &ps);
+        code = TableProc( hwnd, OS.WM_PAINT, cast(int)hDC, lParam);
+        OS.EndPaint (hwnd, &ps);
+    } else {
+        code = TableProc( hwnd, msg, wParam, lParam);
+    }
+    if (fixScroll) {
+        int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE;
+        OS.RedrawWindow (handle, null, null, flags);
+    }
+
     if (checkActivate) ignoreActivate = false;
     if (checkSelection) {
         if (wasSelected || forceSelect) {
@@ -328,6 +401,7 @@
         /* Resize messages */
         case OS.WM_WINDOWPOSCHANGED:
             if (redraw) {
+                OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
                 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
                 OS.InvalidateRect (handle, null, true);
                 auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
@@ -370,13 +444,17 @@
 
 static int checkStyle (int style) {
     /*
-    * Feature in Windows.  It is not possible to create
-    * a table that does not have scroll bars.  Therefore,
-    * no matter what style bits are specified, set the
-    * H_SCROLL and V_SCROLL bits so that the DWT style
-    * will match the widget that Windows creates.
+    * Feature in Windows.  Even when WS_HSCROLL or
+    * WS_VSCROLL is not specified, Windows creates
+    * trees and tables with scroll bars.  The fix
+    * is to set H_SCROLL and V_SCROLL.
+    *
+    * NOTE: This code appears on all platforms so that
+    * applications have consistent scroll bar behavior.
     */
-    style |= DWT.H_SCROLL | DWT.V_SCROLL;
+    if ((style & DWT.NO_SCROLL) is 0) {
+        style |= DWT.H_SCROLL | DWT.V_SCROLL;
+    }
     return checkBits (style, DWT.SINGLE, DWT.MULTI, 0, 0, 0, 0);
 }
 
@@ -497,6 +575,13 @@
                 int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
                 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) is 0) {
                     int bits = OS.LVS_EX_FULLROWSELECT;
+                    /*
+                    * Feature in Windows.  When LVM_SETEXTENDEDLISTVIEWSTYLE is
+                    * used to set or clear the extended style bits and the table
+                    * has a tooltip, the tooltip is hidden.  The fix is to clear
+                    * the tooltip before setting the bits and then reset it.
+                    */
+                    int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0);
                     static if (OS.IsWinCE) {
                         RECT rect;
                         bool damaged = cast(bool) OS.GetUpdateRect (handle, &rect, true);
@@ -511,6 +596,12 @@
                         if (result !is OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
                         OS.DeleteObject (rgn);
                     }
+                    /*
+                    * Bug in Windows.  Despite the documentation, LVM_SETTOOLTIPS
+                    * uses WPARAM instead of LPARAM for the new tooltip  The fix
+                    * is to put the tooltip in both parameters.
+                    */
+                    hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
                 }
             }
         }
@@ -536,6 +627,13 @@
                 int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
                 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) !is 0) {
                     int bits = OS.LVS_EX_FULLROWSELECT;
+                    /*
+                    * Feature in Windows.  When LVM_SETEXTENDEDLISTVIEWSTYLE is
+                    * used to set or clear the extended style bits and the table
+                    * has a tooltip, the tooltip is hidden.  The fix is to clear
+                    * the tooltip before setting the bits and then reset it.
+                    */
+                    auto hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0);
                     static if (OS.IsWinCE) {
                         RECT rect;
                         bool damaged = cast(bool) OS.GetUpdateRect (handle, &rect, true);
@@ -550,6 +648,12 @@
                         if (result !is OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
                         OS.DeleteObject (rgn);
                     }
+                    /*
+                    * Bug in Windows.  Despite the documentation, LVM_SETTOOLTIPS
+                    * uses WPARAM instead of LPARAM for the new tooltip  The fix
+                    * is to put the tooltip in both parameters.
+                    */
+                    hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
                 }
             }
         }
@@ -653,6 +757,12 @@
             sendPaintItemEvent (item, nmcd);
             //widget could be disposed at this point
         }
+        if (!ignoreDrawFocus && focusRect !is null) {
+            OS.SetTextColor (nmcd.nmcd.hdc, 0);
+            OS.SetBkColor (nmcd.nmcd.hdc, 0xFFFFFF);
+            OS.DrawFocusRect (nmcd.nmcd.hdc, focusRect);
+            focusRect = null;
+        }
     }
     return null;
 }
@@ -672,8 +782,7 @@
     */
     TableItem item = _getItem (nmcd.nmcd.dwItemSpec);
     if (item is null) return null;
-    HFONT hFont = item.cellFont !is null ? item.cellFont [nmcd.iSubItem] : cast(HFONT)-1;
-    if (hFont is cast(HFONT)-1) hFont = item.font;
+    HFONT hFont = item.fontHandle (nmcd.iSubItem);
     if (hFont !is cast(HFONT)-1) OS.SelectObject (hDC, hFont);
     if (ignoreCustomDraw || (nmcd.nmcd.rc.left is nmcd.nmcd.rc.right)) {
         return new LRESULT (hFont is cast(HFONT)-1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT);
@@ -682,12 +791,13 @@
     selectionForeground = -1;
     ignoreDrawForeground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawBackground = false;
     if (OS.IsWindowVisible (handle)) {
+        Event measureEvent = null;
         if (hooks (DWT.MeasureItem)) {
-            sendMeasureItemEvent (item, nmcd.nmcd.dwItemSpec, nmcd.iSubItem, nmcd.nmcd.hdc);
+            measureEvent = sendMeasureItemEvent (item, nmcd.nmcd.dwItemSpec, nmcd.iSubItem, nmcd.nmcd.hdc);
             if (isDisposed () || item.isDisposed ()) return null;
         }
         if (hooks (DWT.EraseItem)) {
-            sendEraseItemEvent (item, nmcd, lParam);
+            sendEraseItemEvent (item, nmcd, lParam, measureEvent);
             if (isDisposed () || item.isDisposed ()) return null;
             code |= OS.CDRF_NOTIFYPOSTPAINT;
         }
@@ -723,7 +833,7 @@
                 if ((result !is 0 && (lvItem.state & OS.LVIS_SELECTED) !is 0)) {
                     int clrSelection = -1;
                     if (nmcd.iSubItem is 0) {
-                        if (OS.GetFocus () is handle) {
+                        if (OS.GetFocus () is handle || display.getHighContrast ()) {
                             clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
                         } else {
                             if ((style & DWT.HIDE_SELECTION) is 0) {
@@ -731,7 +841,7 @@
                             }
                         }
                     } else {
-                        if (OS.GetFocus () is handle) {
+                        if (OS.GetFocus () is handle || display.getHighContrast ()) {
                             clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
                             clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
                         } else {
@@ -1172,11 +1282,11 @@
         }
         bits |= width & 0xFFFF;
     }
-    int result = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, -1, bits | 0xFFFF0000);
-    int width = result & 0xFFFF;
-    int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
-    int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
-    int itemHeight = (oneItem >> 16) - (empty >> 16);
+    int /*long*/ result = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, -1, OS.MAKELPARAM (bits, 0xFFFF));
+    int width = OS.LOWORD (result);
+    int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+    int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+    int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
     height += OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0) * itemHeight;
     if (width is 0) width = DEFAULT_WIDTH;
     if (height is 0) height = DEFAULT_HEIGHT;
@@ -1199,7 +1309,7 @@
 
     /* Use the Explorer theme */
     if (EXPLORER_THEME) {
-        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) {
             explorerTheme = true;
             OS.SetWindowTheme (handle, Display.EXPLORER.ptr, null);
         }
@@ -1208,7 +1318,7 @@
     /* Get the header window proc */
     if (HeaderProc is null) {
         auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-        HeaderProc = cast(WNDPROC) OS.GetWindowLong (hwndHeader, OS.GWL_WNDPROC);
+        HeaderProc = cast(WNDPROC) OS.GetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC);
     }
 
     /*
@@ -1237,9 +1347,9 @@
 
     /* Set the checkbox image list */
     if ((style & DWT.CHECK) !is 0) {
-        int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
-        int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
-        int width = (oneItem >> 16) - (empty >> 16), height = width;
+        int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+        int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+        int width = OS.HIWORD (oneItem) - OS.HIWORD (empty), height = width;
         setCheckboxImageList (width, height, false);
         OS.SendMessage (handle, OS. LVM_SETCALLBACKMASK, OS.LVIS_STATEIMAGEMASK, 0);
     }
@@ -1290,22 +1400,30 @@
     *
     * NOTE: WS_EX_LAYOUTRTL is not supported on Windows NT.
     */
-    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return;
-    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
-        auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-        int bits2 = OS.GetWindowLong (hwndHeader, OS.GWL_EXSTYLE);
-        OS.SetWindowLong (hwndHeader, OS.GWL_EXSTYLE, bits2 | OS.WS_EX_LAYOUTRTL);
+    if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+            auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+            int bits2 = OS.GetWindowLong (hwndHeader, OS.GWL_EXSTYLE);
+            OS.SetWindowLong (hwndHeader, OS.GWL_EXSTYLE, bits2 | OS.WS_EX_LAYOUTRTL);
+            auto hwndTooltop = cast(HWND)OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
+            int bits3 = OS.GetWindowLong (hwndTooltop, OS.GWL_EXSTYLE);
+            OS.SetWindowLong (hwndTooltop, OS.GWL_EXSTYLE, bits3 | OS.WS_EX_LAYOUTRTL);
+        }
     }
 }
 
 void createHeaderToolTips () {
     static if (OS.IsWinCE) return;
     if (headerToolTipHandle !is null) return;
+    int bits = 0;
+    if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) bits |= OS.WS_EX_LAYOUTRTL;
+    }
     headerToolTipHandle = OS.CreateWindowEx (
-        0,
+        bits,
         OS.TOOLTIPS_CLASS.ptr,
         null,
-        0,
+        OS.TTS_NOPREFIX,
         OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
         handle,
         null,
@@ -1382,11 +1500,10 @@
                 item.cellForeground = temp;
             }
             if (item.cellFont !is null) {
-                auto cellFont = item.cellFont;
-                HFONT[] temp = new HFONT[]( columnCount + 1 );
+                Font [] cellFont = item.cellFont;
+                Font [] temp = new Font [columnCount + 1];
                 System.arraycopy (cellFont, 0, temp, 0, index);
                 System.arraycopy (cellFont, index, temp, index + 1, columnCount - index);
-                temp [index] = cast(HFONT)-1;
                 item.cellFont = temp;
             }
         }
@@ -1435,7 +1552,18 @@
         } else {
             OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
         }
-        if ((parent.style & DWT.VIRTUAL) is 0) {
+        /*
+        * Bug in Windows.  Despite the fact that every item in the
+        * table always has LPSTR_TEXTCALLBACK, Windows caches the
+        * bounds for the selected items.  This means that
+        * when you change the string to be something else, Windows
+        * correctly asks you for the new string but when the item
+        * is selected, the selection draws using the bounds of the
+        * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+        * even though it has not changed, causing Windows to flush
+        * cached bounds.
+        */
+        if ((style & DWT.VIRTUAL) is 0) {
             LVITEM lvItem;
             lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
             lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
@@ -1713,7 +1841,18 @@
                 OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, &hdItem);
             }
         }
-        if ((parent.style & DWT.VIRTUAL) is 0) {
+        /*
+        * Bug in Windows.  Despite the fact that every item in the
+        * table always has LPSTR_TEXTCALLBACK, Windows caches the
+        * bounds for the selected items.  This means that
+        * when you change the string to be something else, Windows
+        * correctly asks you for the new string but when the item
+        * is selected, the selection draws using the bounds of the
+        * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+        * even though it has not changed, causing Windows to flush
+        * cached bounds.
+        */
+        if ((style & DWT.VIRTUAL) is 0) {
             LVITEM lvItem;
             lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
             lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
@@ -1781,8 +1920,8 @@
                     item.cellForeground = temp;
                 }
                 if (item.cellFont !is null) {
-                    HFONT [] cellFont = item.cellFont;
-                    HFONT [] temp = new HFONT[]( columnCount );
+                    Font [] cellFont = item.cellFont;
+                    Font [] temp = new Font [columnCount];
                     System.arraycopy (cellFont, 0, temp, 0, index);
                     System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
                     item.cellFont = temp;
@@ -1861,7 +2000,7 @@
     if (index is count) return;
     setDeferResize (true);
     ignoreSelect = ignoreShrink = true;
-    int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+    int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
     ignoreSelect = ignoreShrink = false;
     if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
     System.arraycopy (items, index + 1, items, index, --count - index);
@@ -2186,8 +2325,33 @@
 public TableItem getItem (Point point) {
     checkWidget ();
     if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (count is 0) return null;
     LVHITTESTINFO pinfo;
-    pinfo.pt.x = point.x;  pinfo.pt.y = point.y;
+    pinfo.pt.x = point.x;
+    pinfo.pt.y = point.y;
+    if ((style & DWT.FULL_SELECTION) is 0) {
+        if (hooks (DWT.MeasureItem)) {
+            OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, &pinfo);
+            if (pinfo.iItem is -1) {
+                RECT rect;
+                rect.left = OS.LVIR_ICON;
+                ignoreCustomDraw = true;
+                int /*long*/ code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, &rect);
+                ignoreCustomDraw = false;
+                if (code !is 0) {
+                    pinfo.pt.x = rect.left;
+                    OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, &pinfo);
+                }
+            }
+            if (pinfo.iItem !is -1 && pinfo.iSubItem is 0) {
+                if (hitTestSelection (pinfo.iItem, pinfo.pt.x, pinfo.pt.y)) {
+                    return _getItem (pinfo.iItem);
+                }
+            }
+            return null;
+        }
+    }
     OS.SendMessage (handle, OS.LVM_HITTEST, 0, &pinfo);
     if (pinfo.iItem !is -1) {
         /*
@@ -2235,7 +2399,7 @@
 
 /**
  * Returns the height of the area which would be used to
- * display <em>one</em> of the items in the receiver's.
+ * display <em>one</em> of the items in the receiver.
  *
  * @return the height of one item
  *
@@ -2246,9 +2410,9 @@
  */
 public int getItemHeight () {
     checkWidget ();
-    int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
-    int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
-    return (oneItem >> 16) - (empty >> 16);
+    int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+    int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+    return OS.HIWORD (oneItem) - OS.HIWORD (empty);
 }
 
 /**
@@ -2486,6 +2650,28 @@
     return false;
 }
 
+bool hitTestSelection (int index, int x, int y) {
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (count is 0) return false;
+    if (!hooks (DWT.MeasureItem)) return false;
+    bool result = false;
+    if (0 <= index && index < count) {
+        TableItem item = _getItem (index);
+        auto hDC = OS.GetDC (handle);
+        HFONT oldFont, newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont);
+        auto hFont = item.fontHandle (0);
+        if (hFont !is cast(HFONT)-1) hFont = OS.SelectObject (hDC, hFont);
+        Event event = sendMeasureItemEvent (item, index, 0, hDC);
+        if (event.getBounds ().contains (x, y)) result = true;
+        if (hFont !is cast(HFONT)-1) hFont = OS.SelectObject (hDC, hFont);
+        if (newFont !is null) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+//      if (isDisposed () || item.isDisposed ()) return false;
+    }
+    return result;
+}
+
 int imageIndex (Image image, int column) {
     if (image is null) return OS.I_IMAGENONE;
     if (column is 0) {
@@ -2613,6 +2799,15 @@
     return -1;
 }
 
+bool isCustomToolTip () {
+    return hooks (DWT.MeasureItem);
+}
+
+bool isOptimizedRedraw () {
+    if ((style & DWT.H_SCROLL) is 0 || (style & DWT.V_SCROLL) is 0) return false;
+    return !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+}
+
 /**
  * Returns <code>true</code> if the item is selected,
  * and <code>false</code> otherwise.  Indices out of
@@ -2744,7 +2939,7 @@
             TableItem item = items [index];
             if (item !is null && !item.isDisposed ()) item.release (false);
             ignoreSelect = ignoreShrink = true;
-            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+            int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
             ignoreSelect = ignoreShrink = false;
             if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
             System.arraycopy (items, index + 1, items, index, --count - index);
@@ -2778,7 +2973,7 @@
     if (item !is null && !item.isDisposed ()) item.release (false);
     setDeferResize (true);
     ignoreSelect = ignoreShrink = true;
-    int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+    int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
     ignoreSelect = ignoreShrink = false;
     if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
     System.arraycopy (items, index + 1, items, index, --count - index);
@@ -2819,7 +3014,7 @@
             TableItem item = items [index];
             if (item !is null && !item.isDisposed ()) item.release (false);
             ignoreSelect = ignoreShrink = true;
-            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, start, 0);
+            int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, start, 0);
             ignoreSelect = ignoreShrink = false;
             if (code is 0) break;
             index++;
@@ -2873,7 +3068,7 @@
         int index = itemCount - 1;
         while (index >= 0) {
             ignoreSelect = ignoreShrink = true;
-            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+            int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
             ignoreSelect = ignoreShrink = false;
             if (code is 0) break;
             --index;
@@ -2892,7 +3087,7 @@
         if (index !is -1) error (DWT.ERROR_ITEM_NOT_REMOVED);
     } else {
         ignoreSelect = ignoreShrink = true;
-        int code = OS.SendMessage (handle, OS.LVM_DELETEALLITEMS, 0, 0);
+        int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEALLITEMS, 0, 0);
         ignoreSelect = ignoreShrink = false;
         if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
     }
@@ -3067,10 +3262,8 @@
     ignoreSelect = false;
 }
 
-void sendEraseItemEvent (TableItem item, NMLVCUSTOMDRAW* nmcd, int lParam) {
+void sendEraseItemEvent (TableItem item, NMLVCUSTOMDRAW* nmcd, int lParam, Event measureEvent) {
     auto hDC = nmcd.nmcd.hdc;
-    auto hFont = item.cellFont !is null ? item.cellFont [nmcd.iSubItem] : cast(HFONT)-1;
-    if (hFont is cast(HFONT)-1) hFont = item.font;
     int clrText = item.cellForeground !is null ? item.cellForeground [nmcd.iSubItem] : -1;
     if (clrText is -1) clrText = item.foreground;
     int clrTextBk = -1;
@@ -3105,7 +3298,7 @@
     }
     if (OS.IsWindowEnabled (handle)) {
         if (selected && (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0)) {
-            if (OS.GetFocus () is handle) {
+            if (OS.GetFocus () is handle || display.getHighContrast ()) {
                 drawSelected = true;
                 data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
                 data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
@@ -3120,16 +3313,17 @@
         } else {
             drawBackground = clrTextBk !is -1;
             /*
-            * Bug in Windows.  When LVM_SETTEXTBKCOLOR or LVM_SETBKCOLOR
-            * is used to set the background color of the the text or the
-            * control, the color is not set in the HDC that is provided
-            * in Custom Draw.  The fix is to explicitly set the background
-            * color.
+            * Bug in Windows.  When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR
+            * or LVM_SETTEXTCOLOR is used to set the background color of
+            * the the text or the control, the color is not set in the HDC
+            * that is provided in Custom Draw.  The fix is to explicitly
+            * set the color.
             */
-            if (clrTextBk is -1) {
+            if (clrText is -1 || clrTextBk is -1) {
                 Control control = findBackgroundControl ();
                 if (control is null) control = this;
-                clrTextBk = control.getBackgroundPixel ();
+                if (clrText is -1) clrText = control.getForegroundPixel ();
+                if (clrTextBk is -1) clrTextBk = control.getBackgroundPixel ();
             }
             data.foreground = clrText !is -1 ? clrText : OS.GetTextColor (hDC);
             data.background = clrTextBk !is -1 ? clrTextBk : OS.GetBkColor (hDC);
@@ -3139,7 +3333,7 @@
         data.background = OS.GetSysColor (OS.COLOR_3DFACE);
         if (selected) clrSelectionBk = data.background;
     }
-    data.hFont = hFont;
+    data.font = item.getFont (nmcd.iSubItem);
     data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
     auto nSavedDC = OS.SaveDC (hDC);
     GC gc = GC.win32_new (hDC, data);
@@ -3158,6 +3352,7 @@
             }
         }
     }
+    bool focused = (event.detail & DWT.FOCUSED) !is 0;
     if (drawHot) event.detail |= DWT.HOT;
     if (drawSelected) event.detail |= DWT.SELECTED;
     if (drawBackground) event.detail |= DWT.BACKGROUND;
@@ -3196,10 +3391,7 @@
             OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
         }
     }
-    if (ignoreDrawFocus) {
-        nmcd.nmcd.uItemState &= ~OS.CDIS_FOCUS;
-        OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
-    }
+
     auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
     bool firstColumn = nmcd.iSubItem is OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
     if (ignoreDrawForeground && ignoreDrawHot) {
@@ -3208,37 +3400,55 @@
             fillBackground (hDC, clrTextBk, &backgroundRect);
         }
     }
-    if (!ignoreDrawHot || (!ignoreDrawSelection && clrSelectionBk !is -1)) {
+    focusRect = null;
+    if (!ignoreDrawHot || !ignoreDrawSelection || !ignoreDrawFocus) {
+        bool fullText = (style & DWT.FULL_SELECTION) !is 0 || !firstColumn;
+        RECT textRect = item.getBounds (nmcd.nmcd.dwItemSpec, nmcd.iSubItem, true, false, fullText, false, hDC);
+        if ((style & DWT.FULL_SELECTION) is 0) {
+            if (measureEvent !is null) {
+                textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.width);
+            }
+            if (!ignoreDrawFocus) {
+                nmcd.nmcd.uItemState &= ~OS.CDIS_FOCUS;
+                OS.MoveMemory (cast(void*)lParam, &nmcd, NMLVCUSTOMDRAW.sizeof);
+                focusRect = new RECT;
+                *focusRect = textRect;
+            }
+        }
         if (explorerTheme) {
-            bool hot = drawHot;
-            RECT pClipRect;
-            OS.SetRect (&pClipRect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom);
-            RECT rect;
-            OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom);
-            if ((style & DWT.FULL_SELECTION) !is 0) {
-                int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
-                int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
-                RECT headerRect;
-                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, &headerRect);
-                OS.MapWindowPoints (hwndHeader, handle, cast(POINT*) &headerRect, 2);
-                rect.left = 0;
-                rect.right = headerRect.right;
-                pClipRect.left = cellRect.left;
-                pClipRect.right += EXPLORER_EXTRA;
-            } else {
-                rect.right += EXPLORER_EXTRA;
-                pClipRect.right += EXPLORER_EXTRA;
+            if (!ignoreDrawHot || (!ignoreDrawSelection && clrSelectionBk !is -1)) {
+                bool hot = drawHot;
+                RECT pClipRect;
+                OS.SetRect (&pClipRect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom);
+                RECT rect;
+                OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom);
+                if ((style & DWT.FULL_SELECTION) !is 0) {
+                    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                    int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
+                    RECT headerRect;
+                    OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, &headerRect);
+                    OS.MapWindowPoints (hwndHeader, handle, cast(POINT*) &headerRect, 2);
+                    rect.left = 0;
+                    rect.right = headerRect.right;
+                    pClipRect.left = cellRect.left;
+                    pClipRect.right += EXPLORER_EXTRA;
+                } else {
+                    rect.right += EXPLORER_EXTRA;
+                    pClipRect.right += EXPLORER_EXTRA;
+                }
+                auto hTheme = OS.OpenThemeData (handle, Display.LISTVIEW.ptr);
+                int iStateId = selected ? OS.LISS_SELECTED : OS.LISS_HOT;
+                if (OS.GetFocus () !is handle && selected && !hot) iStateId = OS.LISS_SELECTEDNOTFOCUS;
+                OS.DrawThemeBackground (hTheme, hDC, OS.LVP_LISTITEM, iStateId, &rect, &pClipRect);
+                OS.CloseThemeData (hTheme);
             }
-            auto hTheme = OS.OpenThemeData (handle, Display.LISTVIEW.ptr);
-            int iStateId = selected ? OS.LISS_SELECTED : OS.LISS_HOT;
-            if (OS.GetFocus () !is handle && selected && !hot) iStateId = OS.LISS_SELECTEDNOTFOCUS;
-            OS.DrawThemeBackground (hTheme, hDC, OS.LVP_LISTITEM, iStateId, &rect, &pClipRect);
-            OS.CloseThemeData (hTheme);
         } else {
-            bool fullText = ((style & DWT.FULL_SELECTION) !is 0 || !firstColumn);
-            RECT textRect = item.getBounds (nmcd.nmcd.dwItemSpec, nmcd.iSubItem, true, false, fullText, false, hDC);
-            fillBackground (hDC, clrSelectionBk, &textRect);
-        }
+            if (!ignoreDrawSelection && clrSelectionBk !is -1) fillBackground (hDC, clrSelectionBk, &textRect);
+        }
+    }
+    if (focused && ignoreDrawFocus) {
+        nmcd.nmcd.uItemState &= ~OS.CDIS_FOCUS;
+        OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
     }
     if (ignoreDrawForeground) {
         RECT clipRect = item.getBounds (nmcd.nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, false, hDC);
@@ -3248,13 +3458,40 @@
     }
 }
 
-Event sendMeasureItemEvent (TableItem item, int row, int column, HDC hDC) {
-    auto hFont = item.cellFont !is null ? item.cellFont [column] : cast(HFONT)-1;
-    if (hFont is cast(HFONT)-1) hFont = item.font;
+Event sendEraseItemEvent (TableItem item, NMTTCUSTOMDRAW* nmcd, int column, RECT* cellRect) {
+    int nSavedDC = OS.SaveDC (nmcd.nmcd.hdc);
+    RECT* insetRect = toolTipInset (cellRect);
+    OS.SetWindowOrgEx (nmcd.nmcd.hdc, insetRect.left, insetRect.top, null);
     GCData data = new GCData ();
     data.device = display;
-    data.hFont = hFont;
-    auto nSavedDC = OS.SaveDC (hDC);
+    data.foreground = OS.GetTextColor (nmcd.nmcd.hdc);
+    data.background = OS.GetBkColor (nmcd.nmcd.hdc);
+    data.font = item.getFont (column);
+    data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+    GC gc = GC.win32_new (nmcd.nmcd.hdc, data);
+    Event event = new Event ();
+    event.item = item;
+    event.index = column;
+    event.gc = gc;
+    event.detail |= DWT.FOREGROUND;
+    event.x = cellRect.left;
+    event.y = cellRect.top;
+    event.width = cellRect.right - cellRect.left;
+    event.height = cellRect.bottom - cellRect.top;
+    //gc.setClipping (event.x, event.y, event.width, event.height);
+    sendEvent (DWT.EraseItem, event);
+    event.gc = null;
+    //int newTextClr = data.foreground;
+    gc.dispose ();
+    OS.RestoreDC (nmcd.nmcd.hdc, nSavedDC);
+    return event;
+}
+
+Event sendMeasureItemEvent (TableItem item, int row, int column, HDC hDC) {
+    GCData data = new GCData ();
+    data.device = display;
+    data.font = item.getFont (column);
+    int nSavedDC = OS.SaveDC (hDC);
     GC gc = GC.win32_new (hDC, data);
     RECT itemRect = item.getBounds (row, column, true, true, false, false, hDC);
     Event event = new Event ();
@@ -3281,7 +3518,16 @@
     return event;
 }
 
-LRESULT sendMouseDownEvent (int type, int button, int msg, int wParam, int lParam) {
+LRESULT sendMouseDownEvent (int type, int button, int msg, int /*long*/ wParam, int /*long*/ lParam) {
+    Display display = this.display;
+    display.captureChanged = false;
+    if (!sendMouseEvent (type, button, handle, msg, wParam, lParam)) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+        return LRESULT.ZERO;
+    }
+
     /*
     * Feature in Windows.  Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
     * the widget starts a modal loop to determine if the user wants
@@ -3295,16 +3541,30 @@
     * the widget does not eat the mouse up.
     */
     LVHITTESTINFO pinfo;
-    pinfo.pt.x = cast(short) (lParam & 0xFFFF);
-    pinfo.pt.y = cast(short) (lParam >> 16);
+    pinfo.pt.x = OS.GET_X_LPARAM (lParam);
+    pinfo.pt.y = OS.GET_Y_LPARAM (lParam);
     OS.SendMessage (handle, OS.LVM_HITTEST, 0, &pinfo);
-    Display display = this.display;
-    display.captureChanged = false;
-    if (!sendMouseEvent (type, button, handle, msg, wParam, lParam)) {
-        if (!display.captureChanged && !isDisposed ()) {
-            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
-        }
-        return LRESULT.ZERO;
+    if ((style & DWT.FULL_SELECTION) is 0) {
+        if (hooks (DWT.MeasureItem)) {
+            OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, &pinfo);
+            if (pinfo.iItem is -1) {
+                int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+                if (count !is 0) {
+                    RECT rect;
+                    rect.left = OS.LVIR_ICON;
+                    ignoreCustomDraw = true;
+                    int /*long*/ code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, &rect);
+                    ignoreCustomDraw = false;
+                    if (code !is 0) {
+                        pinfo.pt.x = rect.left;
+                        OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, &pinfo);
+                        pinfo.flags &= ~(OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL);
+                    }
+                }
+            } else {
+                if (pinfo.iSubItem !is 0) pinfo.iItem = -1;
+            }
+        }
     }
 
     /*
@@ -3354,6 +3614,21 @@
             forceSelect = true;
         }
     }
+
+    /* Determine whether the user has selected an item based on DWT.MeasureItem */
+    fullRowSelect = false;
+    if (pinfo.iItem !is -1) {
+        if ((style & DWT.FULL_SELECTION) is 0) {
+            if (hooks (DWT.MeasureItem)) {
+                fullRowSelect = hitTestSelection (pinfo.iItem, pinfo.pt.x, pinfo.pt.y);
+                if (fullRowSelect) {
+                    int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
+                    if ((pinfo.flags & flags) !is 0) fullRowSelect = false;
+                }
+            }
+        }
+    }
+
     /*
     * Feature in Windows.  Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
     * the widget starts a modal loop to determine if the user wants
@@ -3366,11 +3641,31 @@
     if (!dragDetect) {
         int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
         dragDetect = pinfo.iItem is -1 || (pinfo.flags & flags) is 0;
-    }
+        if (fullRowSelect) dragDetect = true;
+    }
+
+    /*
+    * Temporarily set LVS_EX_FULLROWSELECT to allow drag and drop
+    * and the mouse to manipulate items based on the results of
+    * the DWT.MeasureItem event.
+    */
+    if (fullRowSelect) {
+        OS.UpdateWindow (handle);
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+        OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, OS.LVS_EX_FULLROWSELECT);
+    }
+    dragStarted = false;
+    display.dragCancelled = false;
     if (!dragDetect) display.runDragDrop = false;
-    int code = callWindowProc (handle, msg, wParam, lParam, forceSelect);
+    int /*long*/ code = callWindowProc (handle, msg, wParam, lParam, forceSelect);
     if (!dragDetect) display.runDragDrop = true;
-    if (dragStarted || !dragDetect) {
+    if (fullRowSelect) {
+        fullRowSelect = false;
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, 0);
+    }
+
+    if (dragStarted || display.dragCancelled) {
         if (!display.captureChanged && !isDisposed ()) {
             if (OS.GetCapture () !is handle) OS.SetCapture (handle);
         }
@@ -3384,7 +3679,6 @@
             sendMouseEvent (DWT.MouseUp, button, handle, msg, wParam, lParam);
         }
     }
-    dragStarted = false;
     return new LRESULT (code);
 }
 
@@ -3392,9 +3686,7 @@
     auto hDC = nmcd.nmcd.hdc;
     GCData data = new GCData ();
     data.device = display;
-    auto hFont = item.cellFont !is null ? item.cellFont [nmcd.iSubItem] : cast(HFONT)-1;
-    if (hFont is cast(HFONT)-1) hFont = item.font;
-    data.hFont = hFont;
+    data.font = item.getFont (nmcd.iSubItem);
     /*
     * Bug in Windows.  For some reason, CDIS_SELECTED always set,
     * even for items that are not selected.  The fix is to get
@@ -3412,7 +3704,7 @@
     }
     if (OS.IsWindowEnabled (handle)) {
         if (selected && (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0)) {
-            if (OS.GetFocus () is handle) {
+            if (OS.GetFocus () is handle || display.getHighContrast ()) {
                 drawSelected = true;
                 if (selectionForeground !is -1) {
                     data.foreground = selectionForeground;
@@ -3437,16 +3729,17 @@
             if (clrTextBk is -1) clrTextBk = item.background;
             drawBackground = clrTextBk !is -1;
             /*
-            * Bug in Windows.  When LVM_SETTEXTBKCOLOR or LVM_SETBKCOLOR
-            * is used to set the background color of the the text or the
-            * control, the color is not set in the HDC that is provided
-            * in Custom Draw.  The fix is to explicitly set the background
-            * color.
+            * Bug in Windows.  When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR
+            * or LVM_SETTEXTCOLOR is used to set the background color of
+            * the the text or the control, the color is not set in the HDC
+            * that is provided in Custom Draw.  The fix is to explicitly
+            * set the color.
             */
-            if (clrTextBk is -1) {
+            if (clrText is -1 || clrTextBk is -1) {
                 Control control = findBackgroundControl ();
                 if (control is null) control = this;
-                clrTextBk = control.getBackgroundPixel ();
+                if (clrText is -1) clrText = control.getForegroundPixel ();
+                if (clrTextBk is -1) clrTextBk = control.getBackgroundPixel ();
             }
             data.foreground = clrText !is -1 ? clrText : OS.GetTextColor (hDC);
             data.background = clrTextBk !is -1 ? clrTextBk : OS.GetBkColor (hDC);
@@ -3485,21 +3778,56 @@
     int cellHeight = cellRect.bottom - cellRect.top;
     gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight);
     sendEvent (DWT.PaintItem, event);
+    if (data.focusDrawn) focusRect = null;
     event.gc = null;
     gc.dispose ();
     OS.RestoreDC (hDC, nSavedDC);
 }
 
-override void setBackgroundImage (HBITMAP hBitmap) {
+Event sendPaintItemEvent (TableItem item, NMTTCUSTOMDRAW* nmcd, int column, RECT* itemRect) {
+    int nSavedDC = OS.SaveDC (nmcd.nmcd.hdc);
+    RECT* insetRect = toolTipInset (itemRect);
+    OS.SetWindowOrgEx (nmcd.nmcd.hdc, insetRect.left, insetRect.top, null);
+    GCData data = new GCData ();
+    data.device = display;
+    data.font = item.getFont (column);
+    data.foreground = OS.GetTextColor (nmcd.nmcd.hdc);
+    data.background = OS.GetBkColor (nmcd.nmcd.hdc);
+    data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+    GC gc = GC.win32_new (nmcd.nmcd.hdc, data);
+    Event event = new Event ();
+    event.item = item;
+    event.index = column;
+    event.gc = gc;
+    event.detail |= DWT.FOREGROUND;
+    event.x = itemRect.left;
+    event.y = itemRect.top;
+    event.width = itemRect.right - itemRect.left;
+    event.height = itemRect.bottom - itemRect.top;
+    //gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight);
+    sendEvent (DWT.PaintItem, event);
+    event.gc = null;
+    gc.dispose ();
+    OS.RestoreDC (nmcd.nmcd.hdc, nSavedDC);
+    return event;
+}
+
+void setBackgroundImage (HBITMAP hBitmap) {
     super.setBackgroundImage (hBitmap);
-    if (!customDraw) setBackgroundTransparent (hBitmap !is null);
+    if (hBitmap !is null) {
+        setBackgroundTransparent (true);
+    } else {
+        if (!hooks (DWT.MeasureItem) && !hooks (DWT.EraseItem) && !hooks (DWT.PaintItem)) {
+            setBackgroundTransparent (false);
+        }
+    }
 }
 
 override void setBackgroundPixel (int newPixel) {
-    if (!customDraw) {
+    int oldPixel = OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
+    if (oldPixel !is OS.CLR_NONE) {
         if (findImageControl () !is null) return;
         if (newPixel is -1) newPixel = defaultBackground ();
-        int oldPixel = OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
         if (oldPixel !is newPixel) {
             OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
             OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
@@ -3565,7 +3893,11 @@
             Control control = findBackgroundControl ();
             if (control is null) control = this;
             if (control.backgroundImage is null) {
-                setBackgroundPixel (control.getBackgroundPixel ());
+                int newPixel = control.getBackgroundPixel ();
+                OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
+                OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
+                if ((style & DWT.CHECK) !is 0) fixCheckboxImageListColor (true);
+                OS.InvalidateRect (handle, null, true);
             }
 
             /* Set LVS_EX_FULLROWSELECT */
@@ -3701,9 +4033,16 @@
     if (defer) {
         if (resizeCount++ is 0) {
             wasResized = false;
+            /*
+            * Feature in Windows.  When LVM_SETBKCOLOR is used with CLR_NONE
+            * to make the background of the table transparent, drawing becomes
+            * slow.  The fix is to temporarily clear CLR_NONE when redraw is
+            * turned off.
+            */
             if (hooks (DWT.MeasureItem) || hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
                 if (drawCount++ is 0 && OS.IsWindowVisible (handle)) {
                     OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+                    OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
                 }
             }
         }
@@ -3711,6 +4050,7 @@
         if (--resizeCount is 0) {
             if (hooks (DWT.MeasureItem) || hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
                 if (--drawCount is 0 /*&& OS.IsWindowVisible (handle)*/) {
+                    OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
                     OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
                     static if (OS.IsWinCE) {
                         auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
@@ -4004,7 +4344,7 @@
         if (item !is null && !item.isDisposed ()) item.release (false);
         if (!isVirtual) {
             ignoreSelect = ignoreShrink = true;
-            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, count, 0);
+            int /*long*/ code = OS.SendMessage (handle, OS.LVM_DELETEITEM, count, 0);
             ignoreSelect = ignoreShrink = false;
             if (code is 0) break;
         }
@@ -4241,25 +4581,23 @@
         int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
         while (index < itemCount) {
             String string = null;
-            HFONT font = cast(HFONT)-1;
+            HFONT hFont = cast(HFONT)-1;
             if (item !is null) {
                 string = item.text;
                 imageIndent = Math.max (imageIndent, item.imageIndent);
-                if (item.cellFont !is null) font = item.cellFont [0];
-                if (font is cast(HFONT)-1) font = item.font;
+                hFont = item.fontHandle (0);
             } else {
                 if (items [index] !is null) {
                     TableItem tableItem = items [index];
                     string = tableItem.text;
                     imageIndent = Math.max (imageIndent, tableItem.imageIndent);
-                    if (tableItem.cellFont !is null) font = tableItem.cellFont [0];
-                    if (font is cast(HFONT)-1) font = tableItem.font;
+                    hFont = tableItem.fontHandle (0);
                 }
             }
             if (string !is null && string.length !is 0) {
-                if (font !is cast(HFONT)-1) {
+                if (hFont !is cast(HFONT)-1) {
                     auto hDC = OS.GetDC (handle);
-                    auto oldFont = OS.SelectObject (hDC, font);
+                    auto oldFont = OS.SelectObject (hDC, hFont);
                     int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
                     TCHAR[] buffer = StrToTCHARs (getCodePage (), string, false);
                     RECT rect;
@@ -4865,8 +5203,31 @@
     super.subclass ();
     if (HeaderProc !is null) {
         auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, display.windowProc);
-    }
+        OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, display.windowProc);
+    }
+}
+
+RECT* toolTipInset (RECT* rect) {
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        RECT* insetRect = new RECT;
+        OS.SetRect (insetRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1);
+        return insetRect;
+    }
+    return rect;
+}
+
+RECT* toolTipRect (RECT* rect) {
+    RECT* toolRect = new RECT;
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        OS.SetRect (toolRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1);
+    } else {
+        HWND hwndToolTip = cast(HWND)OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
+        OS.SetRect (toolRect, rect.left, rect.top, rect.right, rect.bottom);
+        int dwStyle = OS.GetWindowLong (hwndToolTip, OS.GWL_STYLE);
+        int dwExStyle = OS.GetWindowLong (hwndToolTip, OS.GWL_EXSTYLE);
+        OS.AdjustWindowRectEx (toolRect, dwStyle, false, dwExStyle);
+    }
+    return toolRect;
 }
 
 override String toolTipText (NMTTDISPINFO* hdr) {
@@ -4885,7 +5246,7 @@
     super.unsubclass ();
     if (HeaderProc !is null) {
         auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, cast(int) HeaderProc);
+        OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)HeaderProc);
     }
 }
 
@@ -4904,15 +5265,15 @@
     */
     WNDPROC oldHeaderProc, oldTableProc;
     auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-    bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+    bool fixSubclass = isOptimizedRedraw ();
     if (fixSubclass) {
-        oldTableProc = cast(WNDPROC) OS.SetWindowLong (handle, OS.GWL_WNDPROC, cast(int) TableProc);
-        oldHeaderProc = cast(WNDPROC) OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, cast(int) HeaderProc);
+        oldTableProc = cast(WNDPROC)OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TableProc);
+        oldHeaderProc = cast(WNDPROC)OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)HeaderProc);
     }
     super.update (all);
     if (fixSubclass) {
-        OS.SetWindowLong (handle, OS.GWL_WNDPROC, cast(int) oldTableProc);
-        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, cast(int) oldHeaderProc);
+        OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)oldTableProc);
+        OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)oldHeaderProc);
     }
 }
 
@@ -5044,13 +5405,12 @@
             }
             case OS.WM_SETCURSOR: {
                 if (wParam is cast(int)hwnd) {
-                    int hitTest = cast(short) (lParam & 0xFFFF);
+                    int hitTest = cast(short) OS.LOWORD (lParam);
                     if (hitTest is OS.HTCLIENT) {
                         HDHITTESTINFO pinfo;
                         int pos = OS.GetMessagePos ();
                         POINT pt;
-                        pt.x = cast(short) (pos & 0xFFFF);
-                        pt.y = cast(short) (pos >> 16);
+                        OS.POINTSTOPOINT (pt, pos);
                         OS.ScreenToClient (hwnd, &pt);
                         pinfo.pt.x = pt.x;
                         pinfo.pt.y = pt.y;
@@ -5070,6 +5430,19 @@
         }
         return callWindowProc (hwnd, msg, wParam, lParam);
     }
+    if (msg is Display.DI_GETDRAGIMAGE) {
+        /*
+        * Bug in Windows.  For some reason, DI_GETDRAGIMAGE
+        * returns an image that does not contain strings.
+        * The fix is to disable the table window proc.
+        *
+        * NOTE: This only happens on Vista.
+        */
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) return 0;
+        //TEMPORARY CODE
+        if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) return 0;
+//      if (getSelectionCount () !is 1) return 0;
+    }
     return super.windowProc (hwnd, msg, wParam, lParam);
 }
 
@@ -5094,7 +5467,7 @@
             * This allows the application to cancel an operation that is normally
             * performed in WM_KEYDOWN from WM_CHAR.
             */
-            int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
+            int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
             return new LRESULT (code);
         case DWT.CR:
             /*
@@ -5203,18 +5576,18 @@
             * NOTE: The header tooltip can subclass the header proc so the
             * current proc must be restored or header tooltips stop working.
             */
-            int oldHeaderProc = 0, oldTableProc = 0;
+            int /*long*/ oldHeaderProc = 0, oldTableProc = 0;
             auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-            bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+            bool fixSubclass = isOptimizedRedraw ();
             if (fixSubclass) {
-                oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, cast(int) TableProc);
-                oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, cast(int) HeaderProc);
+                oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TableProc);
+                oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)HeaderProc);
             }
-            int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
+            int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
             result = code is 0 ? LRESULT.ZERO : new LRESULT (code);
             if (fixSubclass) {
-                OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
-                OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
+                OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
+                OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
             }
             //FALL THROUGH
         case OS.VK_UP:
@@ -5250,8 +5623,8 @@
     * case and avoid calling the window proc.
     */
     LVHITTESTINFO pinfo;
-    pinfo.pt.x = cast(short) (lParam & 0xFFFF);
-    pinfo.pt.y = cast(short) (lParam >> 16);
+    pinfo.pt.x = OS.GET_X_LPARAM (lParam);
+    pinfo.pt.y = OS.GET_Y_LPARAM (lParam);
     int index = OS.SendMessage (handle, OS.LVM_HITTEST, 0, &pinfo);
     Display display = this.display;
     display.captureChanged = false;
@@ -5302,8 +5675,8 @@
     /* Look for check/uncheck */
     if ((style & DWT.CHECK) !is 0) {
         LVHITTESTINFO pinfo;
-        pinfo.pt.x = cast(short) (lParam & 0xFFFF);
-        pinfo.pt.y = cast(short) (lParam >> 16);
+        pinfo.pt.x = OS.GET_X_LPARAM (lParam);
+        pinfo.pt.y = OS.GET_Y_LPARAM (lParam);
         /*
         * Note that when the table has LVS_EX_FULLROWSELECT and the
         * user clicks anywhere on a row except on the check box, all
@@ -5422,8 +5795,8 @@
     * case and avoid calling the window proc.
     */
     LVHITTESTINFO pinfo;
-    pinfo.pt.x = cast(short) (lParam & 0xFFFF);
-    pinfo.pt.y = cast(short) (lParam >> 16);
+    pinfo.pt.x = OS.GET_X_LPARAM (lParam);
+    pinfo.pt.y = OS.GET_Y_LPARAM (lParam);
     OS.SendMessage (handle, OS.LVM_HITTEST, 0, &pinfo);
     Display display = this.display;
     display.captureChanged = false;
@@ -5507,7 +5880,32 @@
     return result;
 }
 
-override LRESULT WM_SIZE (int wParam, int lParam) {
+override LRESULT WM_SETREDRAW (int wParam, int lParam) {
+    LRESULT result = super.WM_SETREDRAW (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  When LVM_SETBKCOLOR is used with CLR_NONE
+    * to make the background of the table transparent, drawing becomes
+    * slow.  The fix is to temporarily clear CLR_NONE when redraw is
+    * turned off.
+    */
+    if (wParam is 1) {
+        if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) !is OS.CLR_NONE) {
+            if (hooks (DWT.MeasureItem) || hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
+                OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
+            }
+        }
+    }
+    int /*long*/ code = callWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
+    if (wParam is 0) {
+        if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) is OS.CLR_NONE) {
+            OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
+        }
+    }
+    return code is 0 ? LRESULT.ZERO : new LRESULT (code);
+}
+
+override LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) {
     if (ignoreResize) return null;
     if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
         OS.InvalidateRect (handle, null, true);
@@ -5525,8 +5923,11 @@
     if (findBackgroundControl () is null) {
         setBackgroundPixel (defaultBackground ());
     } else {
-        if ((style & DWT.CHECK) !is 0) {
-            fixCheckboxImageListColor (true);
+        int oldPixel = OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
+        if (oldPixel !is OS.CLR_NONE) {
+            if (findImageControl () is null) {
+                if ((style & DWT.CHECK) !is 0) fixCheckboxImageListColor (true);
+            }
         }
     }
     return result;
@@ -5551,27 +5952,78 @@
     }
 
     /*
-    * When there are many columns in a table, scrolling performance
-    * can be improved by temporarily unsubclassing the window proc
-    * so that internal messages are dispatched directly to the table.
-    * If the application expects to see a paint event or has a child
-    * whose font, foreground or background color might be needed,
-    * the window proc cannot be unsubclassed
+    * Feature in Windows.  When there are many columns in a table,
+    * scrolling performance can be improved by unsubclassing the
+    * window proc so that internal messages are dispatched directly
+    * to the table.  If the application expects to see a paint event
+    * or has a child whose font, foreground or background color might
+    * be needed, the window proc cannot be unsubclassed
     *
     * NOTE: The header tooltip can subclass the header proc so the
     * current proc must be restored or header tooltips stop working.
     */
-    int oldHeaderProc = 0, oldTableProc = 0;
+    WNDPROC oldHeaderProc, oldTableProc;
     auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-    bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+    bool fixSubclass = isOptimizedRedraw ();
     if (fixSubclass) {
-        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, cast(int)TableProc);
-        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, cast(int)HeaderProc);
-    }
+        oldTableProc = cast(WNDPROC)OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TableProc);
+        oldHeaderProc = cast(WNDPROC)OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)HeaderProc);
+    }
+
+    /*
+    * Feature in Windows.  For some reason, when the table window
+    * proc processes WM_HSCROLL or WM_VSCROLL when there are many
+    * columns in the table, scrolling is slow and the table does
+    * not keep up with the position of the scroll bar.  The fix
+    * is to turn off redraw, scroll, turn redraw back on and redraw
+    * the entire table.  Strangly, redrawing the entire table is
+    * faster.
+    */
+    bool fixScroll = false;
+    if (OS.LOWORD (wParam) !is OS.SB_ENDSCROLL) {
+        if (OS.COMCTL32_MAJOR >= 6) {
+            if (columnCount > H_SCROLL_LIMIT) {
+                int rowCount = OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0);
+                if (rowCount > V_SCROLL_LIMIT) fixScroll = drawCount is 0 && OS.IsWindowVisible (handle);
+            }
+        }
+    }
+    if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
     LRESULT result = super.WM_HSCROLL (wParam, lParam);
+    if (fixScroll) {
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, null, null, flags);
+        /*
+        * Feature in Windows.  On Vista only, it is faster to
+        * compute and answer the data for the visible columns
+        * of a table when scrolling, rather than just return
+        * the data for each column when asked.
+        */
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            RECT headerRect, rect;
+            OS.GetClientRect (handle, &rect);
+            bool [] visible = new bool [columnCount];
+            for (int i=0; i<columnCount; i++) {
+                visible [i] = true;
+                if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, &headerRect) !is 0) {
+                    OS.MapWindowPoints (hwndHeader, handle, cast(POINT*)&headerRect, 2);
+                    visible [i] = OS.IntersectRect(&headerRect, &rect, &headerRect) !is 0;
+                }
+            }
+            try {
+                display.hwndParent = OS.GetParent (handle);
+                display.columnVisible = visible;
+                OS.UpdateWindow (handle);
+            } finally {
+                display.columnVisible = null;
+            }
+        }
+    }
+
     if (fixSubclass) {
-        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
-        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
+        OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)oldTableProc);
+        OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)oldHeaderProc);
     }
 
     /*
@@ -5609,17 +6061,68 @@
     * NOTE: The header tooltip can subclass the header proc so the
     * current proc must be restored or header tooltips stop working.
     */
-    int oldHeaderProc = 0, oldTableProc = 0;
+    WNDPROC oldHeaderProc, oldTableProc;
     auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
-    bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+    bool fixSubclass = isOptimizedRedraw ();
     if (fixSubclass) {
-        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, cast(int) TableProc);
-        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, cast(int) HeaderProc);
-    }
+        oldTableProc = cast(WNDPROC)OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TableProc);
+        oldHeaderProc = cast(WNDPROC)OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)HeaderProc);
+    }
+
+    /*
+    * Feature in Windows.  For some reason, when the table window
+    * proc processes WM_HSCROLL or WM_VSCROLL when there are many
+    * columns in the table, scrolling is slow and the table does
+    * not keep up with the position of the scroll bar.  The fix
+    * is to turn off redraw, scroll, turn redraw back on and redraw
+    * the entire table.  Strangly, redrawing the entire table is
+    * faster.
+    */
+    bool fixScroll = false;
+    if (OS.LOWORD (wParam) !is OS.SB_ENDSCROLL) {
+        if (OS.COMCTL32_MAJOR >= 6) {
+            if (columnCount > H_SCROLL_LIMIT) {
+                int rowCount = OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0);
+                if (rowCount > V_SCROLL_LIMIT) fixScroll = drawCount is 0 && OS.IsWindowVisible (handle);
+            }
+        }
+    }
+    if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
     LRESULT result = super.WM_VSCROLL (wParam, lParam);
+    if (fixScroll) {
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, null, null, flags);
+        /*
+        * Feature in Windows.  On Vista only, it is faster to
+        * compute and answer the data for the visible columns
+        * of a table when scrolling, rather than just return
+        * the data for each column when asked.
+        */
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            RECT headerRect, rect;
+            OS.GetClientRect (handle, &rect);
+            bool [] visible = new bool [columnCount];
+            for (int i=0; i<columnCount; i++) {
+                visible [i] = true;
+                if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, &headerRect) !is 0) {
+                    OS.MapWindowPoints (hwndHeader, handle, cast(POINT*)&headerRect, 2);
+                    visible [i] = OS.IntersectRect(&headerRect, &rect, &headerRect) !is 0;
+                }
+            }
+            try {
+                display.hwndParent = OS.GetParent (handle);
+                display.columnVisible = visible;
+                OS.UpdateWindow (handle);
+            } finally {
+                display.columnVisible = null;
+            }
+        }
+    }
+
     if (fixSubclass) {
-        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
-        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
+        OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)oldTableProc);
+        OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)oldHeaderProc);
     }
 
     /*
@@ -5630,7 +6133,7 @@
     */
     int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
     if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
-        int code = wParam & 0xFFFF;
+        int code = OS.LOWORD (wParam);
         switch (code) {
             case OS.SB_ENDSCROLL:
             case OS.SB_THUMBPOSITION:
@@ -5646,9 +6149,9 @@
                 RECT clientRect;
                 OS.GetClientRect (handle, &clientRect);
                 clientRect.top += headerHeight;
-                int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
-                int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
-                int itemHeight = (oneItem >> 16) - (empty >> 16);
+                int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+                int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+                int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
                 if (code is OS.SB_LINEDOWN) {
                     clientRect.top = clientRect.bottom - itemHeight - GRID_WIDTH;
                 } else {
@@ -5670,9 +6173,9 @@
     MEASUREITEMSTRUCT* struct_ = cast(MEASUREITEMSTRUCT*)lParam;
     //OS.MoveMemory (struct_, lParam, MEASUREITEMSTRUCT.sizeof);
     if (itemHeight is -1) {
-        int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
-        int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
-        struct_.itemHeight = (oneItem >> 16) - (empty >> 16);
+        int /*long*/ empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+        int /*long*/ oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+        struct_.itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
     } else {
         struct_.itemHeight = itemHeight;
     }
@@ -5681,196 +6184,17 @@
 }
 
 override LRESULT wmNotify (NMHDR* hdr, int wParam, int lParam) {
-    auto hwndHeader = cast(HWND) OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    HWND hwndToolTip = cast(HWND) OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
+    if (hdr.hwndFrom is hwndToolTip) {
+        LRESULT result = wmNotifyToolTip (hdr, wParam, lParam);
+        if (result !is null) return result;
+    }
+    HWND hwndHeader = cast(HWND)OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
     if (hdr.hwndFrom is hwndHeader) {
-        /*
-        * Feature in Windows.  On NT, the automatically created
-        * header control is created as a UNICODE window, not an
-        * ANSI window despite the fact that the parent is created
-        * as an ANSI window.  This means that it sends UNICODE
-        * notification messages to the parent window on NT for
-        * no good reason.  The data and size in the NMHEADER and
-        * HDITEM structs is identical between the platforms so no
-        * different message is actually necessary.  Despite this,
-        * Windows sends different messages.  The fix is to look
-        * for both messages, despite the platform.  This works
-        * because only one will be sent on either platform, never
-        * both.
-        */
-        switch (hdr.code) {
-            case OS.HDN_BEGINTRACKW:
-            case OS.HDN_BEGINTRACKA:
-            case OS.HDN_DIVIDERDBLCLICKW:
-            case OS.HDN_DIVIDERDBLCLICKA: {
-                if (columnCount is 0) return LRESULT.ONE;
-                NMHEADER* phdn = cast(NMHEADER*)lParam;
-                //OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
-                TableColumn column = columns [phdn.iItem];
-                if (column !is null && !column.getResizable ()) {
-                    return LRESULT.ONE;
-                }
-                ignoreColumnMove = true;
-                switch (hdr.code) {
-                    case OS.HDN_DIVIDERDBLCLICKW:
-                    case OS.HDN_DIVIDERDBLCLICKA:
-                        /*
-                        * Bug in Windows.  When the first column of a table does not
-                        * have an image and the user double clicks on the divider,
-                        * Windows packs the column but does not take into account
-                        * the empty space left for the image.  The fix is to measure
-                        * each items ourselves rather than letting Windows do it.
-                        */
-                        bool fixPack = phdn.iItem is 0 && !firstColumnImage;
-                        if (column !is null && (fixPack || hooks (DWT.MeasureItem))) {
-                            column.pack ();
-                            return LRESULT.ONE;
-                        }
-                    default:
-                }
-                break;
-            }
-            case OS.NM_RELEASEDCAPTURE: {
-                if (!ignoreColumnMove) {
-                    for (int i=0; i<columnCount; i++) {
-                        TableColumn column = columns [i];
-                        column.updateToolTip (i);
-                    }
-                }
-                ignoreColumnMove = false;
-                break;
-            }
-            case OS.HDN_BEGINDRAG: {
-                if (ignoreColumnMove) return LRESULT.ONE;
-                int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
-                if ((bits & OS.LVS_EX_HEADERDRAGDROP) is 0) break;
-                if (columnCount is 0) return LRESULT.ONE;
-                NMHEADER* phdn = cast(NMHEADER*)lParam;
-                //OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
-                if (phdn.iItem !is -1) {
-                    TableColumn column = columns [phdn.iItem];
-                    if (column !is null && !column.getMoveable ()) {
-                        ignoreColumnMove = true;
-                        return LRESULT.ONE;
-                    }
-                }
-                break;
-            }
-            case OS.HDN_ENDDRAG: {
-                int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
-                if ((bits & OS.LVS_EX_HEADERDRAGDROP) is 0) break;
-                NMHEADER* phdn = cast(NMHEADER*)lParam;
-                //OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
-                if (phdn.iItem !is -1 && phdn.pitem !is null) {
-                    HDITEM* pitem = cast(HDITEM*)phdn.pitem;
-                    //OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
-                    if ((pitem.mask & OS.HDI_ORDER) !is 0 && pitem.iOrder !is -1) {
-                        if (columnCount is 0) break;
-                        int [] order = new int [columnCount];
-                        OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order.ptr);
-                        int index = 0;
-                        while (index < order.length) {
-                            if (order [index] is phdn.iItem) break;
-                            index++;
-                        }
-                        if (index is order.length) index = 0;
-                        if (index is pitem.iOrder) break;
-                        int start = Math.min (index, pitem.iOrder);
-                        int end = Math.max (index, pitem.iOrder);
-                        ignoreColumnMove = false;
-                        for (int i=start; i<=end; i++) {
-                            TableColumn column = columns [order [i]];
-                            if (!column.isDisposed ()) {
-                                column.postEvent (DWT.Move);
-                            }
-                        }
-                    }
-                }
-                break;
-            }
-            case OS.HDN_ITEMCHANGEDW:
-            case OS.HDN_ITEMCHANGEDA: {
-                /*
-                * Bug in Windows.  When a table has the LVS_EX_GRIDLINES extended
-                * style and the user drags any column over the first column in the
-                * table, making the size become zero, when the user drags a column
-                * such that the size of the first column becomes non-zero, the grid
-                * lines are not redrawn.  The fix is to detect the case and force
-                * a redraw of the first column.
-                */
-                int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
-                if (lastWidth is 0 && width > 0) {
-                    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
-                    if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
-                        RECT rect;
-                        OS.GetClientRect (handle, &rect);
-                        rect.right = rect.left + width;
-                        OS.InvalidateRect (handle, &rect, true);
-                    }
-                }
-                lastWidth = width;
-                if (!ignoreColumnResize) {
-                    NMHEADER* phdn = cast(NMHEADER*)lParam;
-                    //OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
-                    if (phdn.pitem !is null) {
-                        HDITEM* pitem = cast(HDITEM*)phdn.pitem;
-                        //OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
-                        if ((pitem.mask & OS.HDI_WIDTH) !is 0) {
-                            TableColumn column = columns [phdn.iItem];
-                            if (column !is null) {
-                                column.updateToolTip (phdn.iItem);
-                                column.sendEvent (DWT.Resize);
-                                if (isDisposed ()) return LRESULT.ZERO;
-                                /*
-                                * It is possible (but unlikely), that application
-                                * code could have disposed the column in the move
-                                * event.  If this happens, process the move event
-                                * for those columns that have not been destroyed.
-                                */
-                                TableColumn [] newColumns = new TableColumn [columnCount];
-                                System.arraycopy (columns, 0, newColumns, 0, columnCount);
-                                int [] order = new int [columnCount];
-                                OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order.ptr);
-                                bool moved = false;
-                                for (int i=0; i<columnCount; i++) {
-                                    TableColumn nextColumn = newColumns [order [i]];
-                                    if (moved && !nextColumn.isDisposed ()) {
-                                        nextColumn.updateToolTip (order [i]);
-                                        nextColumn.sendEvent (DWT.Move);
-                                    }
-                                    if (nextColumn is column) moved = true;
-                                }
-                            }
-                        }
-                    }
-                }
-                break;
-            }
-            case OS.HDN_ITEMDBLCLICKW:
-            case OS.HDN_ITEMDBLCLICKA: {
-                NMHEADER* phdn = cast(NMHEADER*)lParam;
-                //OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
-                TableColumn column = columns [phdn.iItem];
-                if (column !is null) {
-                    column.postEvent (DWT.DefaultSelection);
-                }
-                break;
-            }
-            default:
-        }
-    }
-    LRESULT result = super.wmNotify (hdr, wParam, lParam);
-    if (result !is null) return result;
-    switch (hdr.code) {
-        case OS.TTN_GETDISPINFOA:
-        case OS.TTN_GETDISPINFOW: {
-            tipRequested = true;
-            int code = callWindowProc (handle, OS.WM_NOTIFY, wParam, lParam);
-            tipRequested = false;
-            return new LRESULT (code);
-        }
-        default:
-    }
-    return result;
+        LRESULT result = wmNotifyHeader (hdr, wParam, lParam);
+        if (result !is null) return result;
+    }
+    return super.wmNotify (hdr, wParam, lParam);
 }
 
 override LRESULT wmNotifyChild (NMHDR* hdr, int wParam, int lParam) {
@@ -5898,6 +6222,11 @@
             NMLVDISPINFO* plvfi = cast(NMLVDISPINFO*)lParam;
             //OS.MoveMemory (plvfi, lParam, NMLVDISPINFO.sizeof);
 
+            bool [] visible = display.columnVisible;
+            if (visible !is null && !visible [plvfi.item.iSubItem]) {
+                break;
+            }
+
             /*
             * When an item is being deleted from a virtual table, do not
             * allow the application to provide data for a new item that
@@ -6056,16 +6385,19 @@
             if (hooks (DWT.MouseDown) || hooks (DWT.MouseUp)) {
                 return LRESULT.ONE;
             }
+            if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+                if (findImageControl () !is null) return LRESULT.ONE;
+            }
             break;
         }
         case OS.LVN_BEGINDRAG:
         case OS.LVN_BEGINRDRAG: {
+            if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) break;
             dragStarted = true;
             if (hdr.code is OS.LVN_BEGINDRAG) {
                 int pos = OS.GetMessagePos ();
                 POINT pt;
-                pt.x = cast(short) (pos & 0xFFFF);
-                pt.y = cast(short) (pos >> 16);
+                OS.POINTSTOPOINT (pt, pos);
                 OS.ScreenToClient (handle, &pt);
                 sendDragEvent (1, pt.x, pt.y);
             }
@@ -6092,6 +6424,11 @@
             break;
         }
         case OS.LVN_ITEMCHANGED: {
+            if (fullRowSelect) {
+                fullRowSelect = false;
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, 0);
+            }
             if (!ignoreSelect) {
                 NMLISTVIEW* pnmlv = cast(NMLISTVIEW*)lParam;
                 //OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof);
@@ -6164,5 +6501,365 @@
     return super.wmNotifyChild (hdr, wParam, lParam);
 }
 
-}
-
+LRESULT wmNotifyHeader (NMHDR* hdr, int /*long*/ wParam, int /*long*/ lParam) {
+    /*
+    * Feature in Windows.  On NT, the automatically created
+    * header control is created as a UNICODE window, not an
+    * ANSI window despite the fact that the parent is created
+    * as an ANSI window.  This means that it sends UNICODE
+    * notification messages to the parent window on NT for
+    * no good reason.  The data and size in the NMHEADER and
+    * HDITEM structs is identical between the platforms so no
+    * different message is actually necessary.  Despite this,
+    * Windows sends different messages.  The fix is to look
+    * for both messages, despite the platform.  This works
+    * because only one will be sent on either platform, never
+    * both.
+    */
+    switch (hdr.code) {
+        case OS.HDN_BEGINTRACKW:
+        case OS.HDN_BEGINTRACKA:
+        case OS.HDN_DIVIDERDBLCLICKW:
+        case OS.HDN_DIVIDERDBLCLICKA: {
+            if (columnCount is 0) return LRESULT.ONE;
+            NMHEADER* phdn = cast(NMHEADER*)lParam;
+            TableColumn column = columns [phdn.iItem];
+            if (column !is null && !column.getResizable ()) {
+                return LRESULT.ONE;
+            }
+            ignoreColumnMove = true;
+            switch (hdr.code) {
+                case OS.HDN_DIVIDERDBLCLICKW:
+                case OS.HDN_DIVIDERDBLCLICKA:
+                    /*
+                    * Bug in Windows.  When the first column of a table does not
+                    * have an image and the user double clicks on the divider,
+                    * Windows packs the column but does not take into account
+                    * the empty space left for the image.  The fix is to measure
+                    * each items ourselves rather than letting Windows do it.
+                    */
+                    bool fixPack = phdn.iItem is 0 && !firstColumnImage;
+                    if (column !is null && (fixPack || hooks (DWT.MeasureItem))) {
+                        column.pack ();
+                        return LRESULT.ONE;
+                    }
+                default:
+            }
+            break;
+        }
+        case OS.NM_RELEASEDCAPTURE: {
+            if (!ignoreColumnMove) {
+                for (int i=0; i<columnCount; i++) {
+                    TableColumn column = columns [i];
+                    column.updateToolTip (i);
+                }
+            }
+            ignoreColumnMove = false;
+            break;
+        }
+        case OS.HDN_BEGINDRAG: {
+            if (ignoreColumnMove) return LRESULT.ONE;
+            int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+            if ((bits & OS.LVS_EX_HEADERDRAGDROP) is 0) break;
+            if (columnCount is 0) return LRESULT.ONE;
+            NMHEADER* phdn = cast(NMHEADER*)lParam;
+            if (phdn.iItem !is -1) {
+                TableColumn column = columns [phdn.iItem];
+                if (column !is null && !column.getMoveable ()) {
+                    ignoreColumnMove = true;
+                    return LRESULT.ONE;
+                }
+            }
+            break;
+        }
+        case OS.HDN_ENDDRAG: {
+            int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+            if ((bits & OS.LVS_EX_HEADERDRAGDROP) is 0) break;
+            NMHEADER* phdn = cast(NMHEADER*)lParam;
+            if (cast(int)phdn.iItem !is -1 && cast(int)phdn.pitem !is 0) {
+                HDITEM* pitem = cast(HDITEM*)phdn.pitem;
+                if ((pitem.mask & OS.HDI_ORDER) !is 0 && pitem.iOrder !is -1) {
+                    if (columnCount is 0) break;
+                    int [] order = new int [columnCount];
+                    OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order.ptr);
+                    int index = 0;
+                    while (index < order.length) {
+                        if (order [index] is phdn.iItem) break;
+                        index++;
+                    }
+                    if (index is order.length) index = 0;
+                    if (index is pitem.iOrder) break;
+                    int start = Math.min (index, pitem.iOrder);
+                    int end = Math.max (index, pitem.iOrder);
+                    ignoreColumnMove = false;
+                    for (int i=start; i<=end; i++) {
+                        TableColumn column = columns [order [i]];
+                        if (!column.isDisposed ()) {
+                            column.postEvent (DWT.Move);
+                        }
+                    }
+                }
+            }
+            break;
+        }
+        case OS.HDN_ITEMCHANGEDW:
+        case OS.HDN_ITEMCHANGEDA: {
+            /*
+            * Bug in Windows.  When a table has the LVS_EX_GRIDLINES extended
+            * style and the user drags any column over the first column in the
+            * table, making the size become zero, when the user drags a column
+            * such that the size of the first column becomes non-zero, the grid
+            * lines are not redrawn.  The fix is to detect the case and force
+            * a redraw of the first column.
+            */
+            int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
+            if (lastWidth is 0 && width > 0) {
+                int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
+                    RECT rect;
+                    OS.GetClientRect (handle, &rect);
+                    rect.right = rect.left + width;
+                    OS.InvalidateRect (handle, &rect, true);
+                }
+            }
+            lastWidth = width;
+            if (!ignoreColumnResize) {
+                NMHEADER* phdn = cast(NMHEADER*)lParam;
+                if (phdn.pitem !is null) {
+                    HDITEM* pitem = cast(HDITEM*)phdn.pitem;
+                    if ((pitem.mask & OS.HDI_WIDTH) !is 0) {
+                        TableColumn column = columns [phdn.iItem];
+                        if (column !is null) {
+                            column.updateToolTip (phdn.iItem);
+                            column.sendEvent (DWT.Resize);
+                            if (isDisposed ()) return LRESULT.ZERO;
+                            /*
+                            * It is possible (but unlikely), that application
+                            * code could have disposed the column in the move
+                            * event.  If this happens, process the move event
+                            * for those columns that have not been destroyed.
+                            */
+                            TableColumn [] newColumns = new TableColumn [columnCount];
+                            System.arraycopy (columns, 0, newColumns, 0, columnCount);
+                            int [] order = new int [columnCount];
+                            OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order.ptr);
+                            bool moved = false;
+                            for (int i=0; i<columnCount; i++) {
+                                TableColumn nextColumn = newColumns [order [i]];
+                                if (moved && !nextColumn.isDisposed ()) {
+                                    nextColumn.updateToolTip (order [i]);
+                                    nextColumn.sendEvent (DWT.Move);
+                                }
+                                if (nextColumn is column) moved = true;
+                            }
+                        }
+                    }
+                }
+            }
+            break;
+        }
+        case OS.HDN_ITEMDBLCLICKW:
+        case OS.HDN_ITEMDBLCLICKA: {
+            NMHEADER* phdn = cast(NMHEADER*)lParam;
+            TableColumn column = columns [phdn.iItem];
+            if (column !is null) {
+                column.postEvent (DWT.DefaultSelection);
+            }
+            break;
+        }
+        default:
+    }
+    return null;
+}
+
+LRESULT wmNotifyToolTip (NMHDR* hdr, int /*long*/ wParam, int /*long*/ lParam) {
+    if (OS.IsWinCE) return null;
+    switch (hdr.code) {
+        case OS.NM_CUSTOMDRAW: {
+            if (toolTipText_ !is null) break;
+            if (isCustomToolTip ()) {
+                NMTTCUSTOMDRAW* nmcd = cast(NMTTCUSTOMDRAW*)lParam;
+                return wmNotifyToolTip (nmcd, lParam);
+            }
+            break;
+        }
+        case OS.TTN_GETDISPINFOA:
+        case OS.TTN_GETDISPINFOW:
+        case OS.TTN_SHOW: {
+            LRESULT result = super.wmNotify (hdr, wParam, lParam);
+            if (result !is null) return result;
+            if (hdr.code !is OS.TTN_SHOW) tipRequested = true;
+            int /*long*/ code = callWindowProc (handle, OS.WM_NOTIFY, wParam, lParam);
+            if (hdr.code !is OS.TTN_SHOW) tipRequested = false;
+            if (toolTipText_ !is null) break;
+            if (isCustomToolTip ()) {
+                LVHITTESTINFO pinfo;
+                int pos = OS.GetMessagePos ();
+                POINT pt;
+                OS.POINTSTOPOINT (pt, pos);
+                OS.ScreenToClient (handle, &pt);
+                pinfo.pt.x = pt.x;
+                pinfo.pt.y = pt.y;
+                if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, &pinfo) !is -1) {
+                    TableItem item = _getItem (pinfo.iItem);
+                    auto hDC = OS.GetDC (handle);
+                    HFONT oldFont, newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                    if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont);
+                    auto hFont = item.fontHandle (pinfo.iSubItem);
+                    if (cast(int)hFont !is -1) hFont = cast(HFONT)OS.SelectObject (hDC, hFont);
+                    Event event = sendMeasureItemEvent (item, pinfo.iItem, pinfo.iSubItem, hDC);
+                    if (!isDisposed () && !item.isDisposed ()) {
+                        RECT itemRect;
+                        OS.SetRect (&itemRect, event.x, event.y, event.x + event.width, event.y + event.height);
+                        if (hdr.code is OS.TTN_SHOW) {
+                            RECT* toolRect = toolTipRect (&itemRect);
+                            OS.MapWindowPoints (handle, null, cast(POINT*)toolRect, 2);
+                            auto hwndToolTip = cast(HWND) OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
+                            int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER;
+                            int width = toolRect.right - toolRect.left, height = toolRect.bottom - toolRect.top;
+                            SetWindowPos (hwndToolTip, null, toolRect.left , toolRect.top, width, height, flags);
+                        } else {
+                            NMTTDISPINFO* lpnmtdi = null;
+                            if (hdr.code is OS.TTN_GETDISPINFOA) {
+                                lpnmtdi = cast(NMTTDISPINFO*)new NMTTDISPINFOA;
+                                OS.MoveMemory (lpnmtdi, cast(void*) lParam, NMTTDISPINFOA.sizeof);
+                                if (lpnmtdi.lpszText !is null) {
+                                    (cast(char*)lpnmtdi.lpszText)[0] = 0;
+                                }
+                            } else {
+                                lpnmtdi = cast(NMTTDISPINFO*)new NMTTDISPINFOW;
+                                OS.MoveMemory (lpnmtdi, cast(void*) lParam, NMTTDISPINFOW.sizeof);
+                                if (lpnmtdi.lpszText !is null) {
+                                    (cast(wchar*)lpnmtdi.lpszText)[0] = 0;
+                                }
+                            }
+                            RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, true, true, hDC);
+                            if (itemRect.right > cellRect.right) {
+                                //TEMPORARY CODE
+                                String string = " ";
+//                              String string = null;
+//                              if (pinfo.iSubItem is 0) {
+//                                  string = item.text;
+//                              } else {
+//                                  String [] strings  = item.strings;
+//                                  if (strings !is null) string = strings [pinfo.iSubItem];
+//                              }
+                                if (string !is null) {
+                                    Shell shell = getShell ();
+                                    wchar [] chars = StrToTCHARs(string, true );
+                                    if (hdr.code is OS.TTN_GETDISPINFOA) {
+                                        char [] bytes = new char [chars.length * 2];
+                                        OS.WideCharToMultiByte (getCodePage (), 0, chars.ptr, chars.length, bytes.ptr, bytes.length, null, null);
+                                        shell.setToolTipText (lpnmtdi, bytes);
+                                        OS.MoveMemory (lParam, cast(NMTTDISPINFOA*)lpnmtdi, NMTTDISPINFOA.sizeof);
+                                    } else {
+                                        shell.setToolTipText (lpnmtdi, chars);
+                                        OS.MoveMemory (lParam, cast(NMTTDISPINFOW*)lpnmtdi, NMTTDISPINFOW.sizeof);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (hFont !is cast(HFONT)-1) hFont = OS.SelectObject (hDC, hFont);
+                    if (newFont !is null) OS.SelectObject (hDC, oldFont);
+                    OS.ReleaseDC (handle, hDC);
+                }
+            }
+            return new LRESULT (code);
+        }
+    }
+    return null;
+}
+
+LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW* nmcd, int /*long*/ lParam) {
+    if (OS.IsWinCE) return null;
+    switch (nmcd.nmcd.dwDrawStage) {
+        case OS.CDDS_PREPAINT: {
+            if (isCustomToolTip ()) {
+                //TEMPORARY CODE
+//              nmcd.uDrawFlags |= OS.DT_CALCRECT;
+//              OS.MoveMemory (lParam, nmcd, NMTTCUSTOMDRAW.sizeof);
+                return new LRESULT (OS.CDRF_NOTIFYPOSTPAINT | OS.CDRF_NEWFONT);
+            }
+            break;
+        }
+        case OS.CDDS_POSTPAINT: {
+            LVHITTESTINFO pinfo;
+            int pos = OS.GetMessagePos ();
+            POINT pt;
+            OS.POINTSTOPOINT (pt, pos);
+            OS.ScreenToClient (handle, &pt);
+            pinfo.pt.x = pt.x;
+            pinfo.pt.y = pt.y;
+            if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, &pinfo) !is -1) {
+                TableItem item = _getItem (pinfo.iItem);
+                auto hDC = OS.GetDC (handle);
+                auto hFont = item.fontHandle (pinfo.iSubItem);
+                if (cast(int)hFont is -1) hFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                auto oldFont = cast(HFONT)OS.SelectObject (hDC, hFont);
+                bool drawForeground = true;
+                RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, false, false, hDC);
+                if (hooks (DWT.EraseItem)) {
+                    Event event = sendEraseItemEvent (item, nmcd, pinfo.iSubItem, &cellRect);
+                    if (isDisposed () || item.isDisposed ()) break;
+                    if (event.doit) {
+                        drawForeground = (event.detail & DWT.FOREGROUND) !is 0;
+                    } else {
+                        drawForeground = false;
+                    }
+                }
+                if (drawForeground) {
+                    int nSavedDC = OS.SaveDC (nmcd.nmcd.hdc);
+                    int gridWidth = getLinesVisible () ? Table.GRID_WIDTH : 0;
+                    RECT* insetRect = toolTipInset (&cellRect);
+                    OS.SetWindowOrgEx (nmcd.nmcd.hdc, insetRect.left, insetRect.top, null);
+                    GCData data = new GCData ();
+                    data.device = display;
+                    data.foreground = OS.GetTextColor (nmcd.nmcd.hdc);
+                    data.background = OS.GetBkColor (nmcd.nmcd.hdc);
+                    data.font = Font.win32_new (display, hFont);
+                    GC gc = GC.win32_new (nmcd.nmcd.hdc, data);
+                    int x = cellRect.left;
+                    if (pinfo.iSubItem !is 0) x -= gridWidth;
+                    Image image = item.getImage (pinfo.iSubItem);
+                    if (image !is null) {
+                        Rectangle rect = image.getBounds ();
+                        RECT imageRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, false, true, false, false, hDC);
+                        Point size = imageList is null ? new Point (rect.width, rect.height) : imageList.getImageSize ();
+                        int y = imageRect.top;
+                        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                            y = y + Math.max (0, (imageRect.bottom - imageRect.top - size.y) / 2);
+                        }
+                        gc.drawImage (image, rect.x, rect.y, rect.width, rect.height, x, y, size.x, size.y);
+                        x += size.x + INSET + (pinfo.iSubItem is 0 ? -2 : 4);
+                    } else {
+                        x += INSET + 2;
+                    }
+                    String string = item.getText (pinfo.iSubItem);
+                    if (string !is null) {
+                        int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
+                        TableColumn column = columns !is null ? columns [pinfo.iSubItem] : null;
+                        if (column !is null) {
+                            if ((column.style & DWT.CENTER) !is 0) flags |= OS.DT_CENTER;
+                            if ((column.style & DWT.RIGHT) !is 0) flags |= OS.DT_RIGHT;
+                        }
+                        TCHAR[] buffer = StrToTCHARs (getCodePage (), string, false);
+                        RECT textRect;
+                        OS.SetRect (&textRect, x, cellRect.top, cellRect.right, cellRect.bottom);
+                        OS.DrawText (nmcd.nmcd.hdc, buffer.ptr, buffer.length, &textRect, flags);
+                    }
+                    gc.dispose ();
+                    OS.RestoreDC (nmcd.nmcd.hdc, nSavedDC);
+                }
+                if (hooks (DWT.PaintItem)) {
+                    RECT itemRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, false, false, hDC);
+                    sendPaintItemEvent (item, nmcd, pinfo.iSubItem, &itemRect);
+                }
+                OS.SelectObject (hDC, oldFont);
+                OS.ReleaseDC (handle, hDC);
+            }
+        }
+    }
+    return null;
+}
+}