BugĀ 510728 - [CSS] Allow styling of tree headers via CSS

Add the API for all platforms and implemented Windows.

Change-Id: I714b8961c9db7c9648a29b0b4cb638cf4f16942f
Signed-off-by: Conrad Groth <info@conrad-groth.de>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java
index c2dc4e5..a59c10e 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java
@@ -2621,10 +2621,9 @@
  * Sets the header background color to the color specified
  * by the argument, or to the default system color if the argument is null.
  * <p>
- * Note: This is custom paint operation and only on Windows and GTK3
- * platforms table header background can be changed. If the native header
- * has a 3D look an feel (e.g. Windows 7), this method will cause the header
- * to look FLAT irrespective of the state of the table style.
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the table style.
  * </p>
  * @param color the new color (or null)
  *
@@ -2654,7 +2653,9 @@
  * Sets the header foreground color to the color specified
  * by the argument, or to the default system color if the argument is null.
  * <p>
- * Note: This is custom paint operation and only Windows table header foreground can be changed.
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the table style.
  * </p>
  * @param color the new color (or null)
  *
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java
index 672e6ac..2039e16 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -88,6 +88,7 @@
 	Rectangle imageBounds;
 	TreeItem insertItem;
 	boolean insertBefore;
+	double /*float*/ [] headerBackground, headerForeground;
 
 	/* Used to control drop feedback when DND.FEEDBACK_EXPAND and DND.FEEDBACK_SCROLL is set/not set */
 	boolean shouldExpand = true, shouldScroll = true;
@@ -1542,6 +1543,46 @@
 }
 
 /**
+ * Returns the header background color.
+ *
+ * @return the receiver's header background color.
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public Color getHeaderBackground () {
+	checkWidget ();
+	return getHeaderBackgroundColor ();
+}
+
+private Color getHeaderBackgroundColor () {
+	return headerBackground != null ? Color.cocoa_new (display, headerBackground) : defaultBackground ();
+}
+
+/**
+ * Returns the header foreground color.
+ *
+ * @return the receiver's header foreground color.
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public Color getHeaderForeground () {
+	checkWidget ();
+	return getHeaderForegroundColor ();
+}
+
+private Color getHeaderForegroundColor () {
+	return headerForeground != null ? Color.cocoa_new (display, headerForeground) : defaultForeground ();
+}
+
+/**
  * Returns the height of the receiver's header
  *
  * @return the height of the header or zero if the header is not visible
@@ -2866,6 +2907,70 @@
 }
 
 /**
+ * Sets the header background color to the color specified
+ * by the argument, or to the default system color if the argument is null.
+ * <p>
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the tree style.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public void setHeaderBackground (Color color) {
+	checkWidget ();
+	if (color != null) {
+		if (color.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+	}
+	double /*float*/ [] headerBackground = color != null ? color.handle : null;
+	if (equals (headerBackground, this.headerBackground)) return;
+	this.headerBackground = headerBackground;
+	if (getHeaderVisible()) {
+		redrawWidget (view, false);
+	}
+}
+
+/**
+ * Sets the header foreground color to the color specified
+ * by the argument, or to the default system color if the argument is null.
+ * <p>
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the tree style.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public void setHeaderForeground (Color color) {
+	checkWidget ();
+	if (color != null) {
+		if (color.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+	}
+	double /*float*/ [] headerForeground = color != null ? color.handle : null;
+	if (equals (headerForeground, this.headerForeground)) return;
+	this.headerForeground = headerForeground;
+	if (getHeaderVisible()) {
+		redrawWidget (view, false);
+	}
+}
+
+/**
  * Marks the receiver's header as visible if the argument is <code>true</code>,
  * and marks it invisible otherwise.
  * <p>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
index cea7494..21da5ed 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
@@ -3415,10 +3415,9 @@
  * Sets the header background color to the color specified
  * by the argument, or to the default system color if the argument is null.
  * <p>
- * Note: This is custom paint operation and only on Windows and GTK3
- * platforms table header background can be changed. If the native header
- * has a 3D look an feel (e.g. Windows 7), this method will cause the header
- * to look FLAT irrespective of the state of the table style.
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the table style.
  * </p>
  * @param color the new color (or null)
  *
@@ -3453,23 +3452,27 @@
 		headerCSSBackground = css;
 		String finalCss = display.gtk_css_create_css_color_string (headerCSSBackground, headerCSSForeground, SWT.BACKGROUND);
 		for (TableColumn column : columns) {
-			long /*int*/ context = OS.gtk_widget_get_style_context(column.buttonHandle);
-			// Create provider as we need it attached to the proper context which is not the widget one
-			long /*int*/ provider = OS.gtk_css_provider_new ();
-			OS.gtk_style_context_add_provider (context, provider, OS.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-			OS.g_object_unref (provider);
-			OS.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (finalCss, true), -1, null);
-			OS.gtk_style_context_invalidate(context);
+			if (column != null) {
+				long /*int*/ context = OS.gtk_widget_get_style_context(column.buttonHandle);
+				// Create provider as we need it attached to the proper context which is not the widget one
+				long /*int*/ provider = OS.gtk_css_provider_new ();
+				OS.gtk_style_context_add_provider (context, provider, OS.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+				OS.g_object_unref (provider);
+				OS.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (finalCss, true), -1, null);
+				OS.gtk_style_context_invalidate(context);
+			}
 		}
 	}
-	// redrawn not necessary, GTK handles the css update
+	// Redraw not necessary, GTK handles the CSS update.
 }
 
 /**
  * Sets the header foreground color to the color specified
  * by the argument, or to the default system color if the argument is null.
  * <p>
- * Note: This is custom paint operation and only Windows table header foreground can be changed.
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the table style.
  * </p>
  * @param color the new color (or null)
  *
@@ -3503,16 +3506,18 @@
 		headerCSSForeground = css;
 		String finalCss = display.gtk_css_create_css_color_string (headerCSSBackground, headerCSSForeground, SWT.FOREGROUND);
 		for (TableColumn column : columns) {
-			long /*int*/ context = OS.gtk_widget_get_style_context(column.buttonHandle);
-			// Create provider as we need it attached to the proper context which is not the widget one
-			long /*int*/ provider = OS.gtk_css_provider_new ();
-			OS.gtk_style_context_add_provider (context, provider, OS.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-			OS.g_object_unref (provider);
-			OS.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (finalCss, true), -1, null);
-			OS.gtk_style_context_invalidate(context);
+			if (column != null) {
+				long /*int*/ context = OS.gtk_widget_get_style_context(column.buttonHandle);
+				// Create provider as we need it attached to the proper context which is not the widget one
+				long /*int*/ provider = OS.gtk_css_provider_new ();
+				OS.gtk_style_context_add_provider (context, provider, OS.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+				OS.g_object_unref (provider);
+				OS.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (finalCss, true), -1, null);
+				OS.gtk_style_context_invalidate(context);
+			}
 		}
 	}
-	// redrawn not necessary, GTK handles the css update
+	// Redraw not necessary, GTK handles the CSS update.
 }
 
 /**
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
index f2800e1..9bf1b1d 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -95,6 +95,8 @@
 	int pixbufHeight, pixbufWidth;
 	TreeItem topItem;
 	double cachedAdjustment, currentAdjustment;
+	Color headerBackground, headerForeground;
+	String headerCSSBackground, headerCSSForeground;
 
 	static final int ID_COLUMN = 0;
 	static final int CHECKED_COLUMN = 1;
@@ -1448,6 +1450,38 @@
 }
 
 /**
+ * Returns the header background color.
+ *
+ * @return the receiver's header background color.
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public Color getHeaderBackground () {
+	checkWidget ();
+	return headerBackground != null ? headerBackground : display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+}
+
+/**
+ * Returns the header foreground color.
+ *
+ * @return the receiver's header foreground color.
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public Color getHeaderForeground () {
+	checkWidget ();
+	return headerForeground != null ? headerForeground : display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
+}
+
+/**
  * Returns the height of the receiver's header
  *
  * @return the height of the header or zero if the header is not visible
@@ -3421,6 +3455,115 @@
 }
 
 /**
+ * Sets the header background color to the color specified
+ * by the argument, or to the default system color if the argument is null.
+ * <p>
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the tree style.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public void setHeaderBackground (Color color) {
+	checkWidget();
+	if (color != null) {
+		if (color.isDisposed ())
+			error(SWT.ERROR_INVALID_ARGUMENT);
+		if (color.equals(headerBackground))
+			return;
+	} else if (headerBackground == null) return;
+	headerBackground = color;
+	if (OS.GTK3) {
+		GdkRGBA background;
+		if (headerBackground != null) {
+			background = display.toGdkRGBA(headerBackground.handle);
+		} else {
+			background = display.toGdkRGBA(display.COLOR_LIST_BACKGROUND);
+		}
+		String name = OS.GTK_VERSION >= OS.VERSION(3, 20, 0) ? "button" : "GtkButton";
+		// background works for 3.18 and later, background-color only as of 3.20
+		String css = name + " {background: " + display.gtk_rgba_to_css_string(background) + ";}\n";
+		headerCSSBackground = css;
+		String finalCss = display.gtk_css_create_css_color_string (headerCSSBackground, headerCSSForeground, SWT.BACKGROUND);
+		for (TreeColumn column : columns) {
+			if (column != null) {
+				long /*int*/ context = OS.gtk_widget_get_style_context(column.buttonHandle);
+				// Create provider as we need it attached to the proper context which is not the widget one
+				long /*int*/ provider = OS.gtk_css_provider_new ();
+				OS.gtk_style_context_add_provider (context, provider, OS.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+				OS.g_object_unref (provider);
+				OS.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (finalCss, true), -1, null);
+				OS.gtk_style_context_invalidate(context);
+			}
+		}
+	}
+	// Redraw not necessary, GTK handles the CSS update.
+}
+
+/**
+ * Sets the header foreground color to the color specified
+ * by the argument, or to the default system color if the argument is null.
+ * <p>
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the tree style.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public void setHeaderForeground (Color color) {
+	checkWidget();
+	if (color != null) {
+		if (color.isDisposed ())
+			error(SWT.ERROR_INVALID_ARGUMENT);
+		if (color.equals(headerForeground))
+			return;
+	} else if (headerForeground == null) return;
+	headerForeground = color;
+	if (OS.GTK3) {
+		GdkRGBA foreground;
+		if (headerForeground != null) {
+			foreground = display.toGdkRGBA(headerForeground.handle);
+		} else {
+			foreground = display.toGdkRGBA(display.COLOR_LIST_FOREGROUND);
+		}
+		String name = OS.GTK_VERSION >= OS.VERSION(3, 20, 0) ? "button" : "GtkButton";
+		String css = name + " {color: " + display.gtk_rgba_to_css_string(foreground) + ";}";
+		headerCSSForeground = css;
+		String finalCss = display.gtk_css_create_css_color_string (headerCSSBackground, headerCSSForeground, SWT.FOREGROUND);
+		for (TreeColumn column : columns) {
+			if (column != null) {
+				long /*int*/ context = OS.gtk_widget_get_style_context(column.buttonHandle);
+				// Create provider as we need it attached to the proper context which is not the widget one
+				long /*int*/ provider = OS.gtk_css_provider_new ();
+				OS.gtk_style_context_add_provider (context, provider, OS.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+				OS.g_object_unref (provider);
+				OS.gtk_css_provider_load_from_data (provider, Converter.wcsToMbcs (finalCss, true), -1, null);
+				OS.gtk_style_context_invalidate(context);
+			}
+		}
+	}
+	// Redraw not necessary, GTK handles the CSS update.
+}
+
+/**
  * Marks the receiver's header as visible if the argument is <code>true</code>,
  * and marks it invisible otherwise.
  * <p>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java
index cc6214b..bf24847 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java
@@ -4641,10 +4641,9 @@
  * Sets the header background color to the color specified
  * by the argument, or to the default system color if the argument is null.
  * <p>
- * Note: This is custom paint operation and only on Windows and GTK3
- * platforms table header background can be changed. If the native header
- * has a 3D look an feel (e.g. Windows 7), this method will cause the header
- * to look FLAT irrespective of the state of the table style.
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the table style.
  * </p>
  * @param color the new color (or null)
  *
@@ -4675,7 +4674,9 @@
  * Sets the header foreground color to the color specified
  * by the argument, or to the default system color if the argument is null.
  * <p>
- * Note: This is custom paint operation and only Windows table header foreground can be changed.
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the table style.
  * </p>
  * @param color the new color (or null)
  *
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java
index 63d4d66..c41a8d3 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -93,10 +93,13 @@
 	boolean ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus;
 	boolean ignoreDrawSelection, ignoreDrawHot, ignoreFullSelection, explorerTheme;
 	boolean createdAsRTL;
+	boolean headerItemDragging;
 	int scrollWidth, selectionForeground;
 	long /*int*/ headerToolTipHandle, itemToolTipHandle;
 	long /*int*/ lastTimerID = -1;
 	int lastTimerCount;
+	int headerBackground = -1;
+	int headerForeground = -1;
 	static final boolean ENABLE_TVS_EX_FADEINOUTEXPANDOS = System.getProperty("org.eclipse.swt.internal.win32.enableFadeInOutExpandos") != null;
 	static final int TIMER_MAX_COUNT = 8;
 	static final int INSET = 3;
@@ -2341,6 +2344,10 @@
 	itemCount = -1;
 }
 
+private boolean customHeaderDrawing() {
+	return headerBackground != -1 || headerForeground != -1;
+}
+
 @Override
 int defaultBackground () {
 	return OS.GetSysColor (OS.COLOR_WINDOW);
@@ -2932,6 +2939,46 @@
 }
 
 /**
+ * Returns the header background color.
+ *
+ * @return the receiver's header background color.
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public Color getHeaderBackground () {
+	checkWidget ();
+	return Color.win32_new (display, getHeaderBackgroundPixel());
+}
+
+private int getHeaderBackgroundPixel() {
+	return headerBackground != -1 ? headerBackground : defaultBackground();
+}
+
+/**
+ * Returns the header foreground color.
+ *
+ * @return the receiver's header foreground color.
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public Color getHeaderForeground () {
+	checkWidget ();
+	return Color.win32_new (display, getHeaderForegroundPixel());
+}
+
+private int getHeaderForegroundPixel() {
+	return headerForeground != -1 ? headerForeground : defaultForeground();
+}
+
+/**
  * Returns the height of the receiver's header
  *
  * @return the height of the header or zero if the header is not visible
@@ -4279,6 +4326,7 @@
 	linesVisible = show;
 	if (hwndParent == 0 && linesVisible) customDraw = true;
 	OS.InvalidateRect (handle, null, true);
+	OS.InvalidateRect (hwndHeader, null, true);
 }
 
 @Override
@@ -4783,6 +4831,72 @@
 }
 
 /**
+ * Sets the header background color to the color specified
+ * by the argument, or to the default system color if the argument is null.
+ * <p>
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the tree style.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public void setHeaderBackground (Color color) {
+	checkWidget ();
+	int pixel = -1;
+	if (color != null) {
+		if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+		pixel = color.handle;
+	}
+	if (pixel == headerBackground) return;
+	headerBackground = pixel;
+	if (getHeaderVisible()) {
+		OS.InvalidateRect (hwndHeader, null, true);
+	}
+}
+
+/**
+ * Sets the header foreground color to the color specified
+ * by the argument, or to the default system color if the argument is null.
+ * <p>
+ * Note: This operation is a hint and is not supported on all platforms. If
+ * the native header has a 3D look and feel (e.g. Windows 7), this method
+ * will cause the header to look FLAT irrespective of the state of the tree style.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 3.106
+ */
+public void setHeaderForeground (Color color) {
+	checkWidget ();
+	int pixel = -1;
+	if (color != null) {
+		if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+		pixel = color.handle;
+	}
+	if (pixel == headerForeground) return;
+	headerForeground = pixel;
+	if (getHeaderVisible()) {
+		OS.InvalidateRect (hwndHeader, null, true);
+	}
+}
+
+/**
  * Marks the receiver's header as visible if the argument is <code>true</code>,
  * and marks it invisible otherwise.
  * <p>
@@ -7853,6 +7967,142 @@
 			}
 			break;
 		}
+		case OS.NM_CUSTOMDRAW: {
+			NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW();
+			OS.MoveMemory(nmcd, lParam, NMCUSTOMDRAW.sizeof);
+			switch (nmcd.dwDrawStage) {
+				case OS.CDDS_PREPAINT: {
+					/* Drawing here will be deleted by further drawing steps, even with OS.CDRF_SKIPDEFAULT.
+					   Changing the TextColor and returning OS.CDRF_NEWFONT has no effect. */
+					return new LRESULT (customHeaderDrawing() ? OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT : OS.CDRF_DODEFAULT);
+				}
+				case OS.CDDS_ITEMPREPAINT: {
+					// draw background
+					RECT rect = new RECT();
+					OS.SetRect(rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+					int pixel = getHeaderBackgroundPixel();
+					if ((nmcd.uItemState & OS.CDIS_SELECTED) != 0) {
+						pixel = getDifferentColor(pixel);
+					} else if (columns[(int) nmcd.dwItemSpec] == sortColumn && sortDirection != SWT.NONE) {
+						pixel = getSlightlyDifferentColor(pixel);
+					}
+					long /*int*/ brush = OS.CreateSolidBrush(pixel);
+					OS.FillRect(nmcd.hdc, rect, brush);
+					OS.DeleteObject(brush);
+
+					return new LRESULT(OS.CDRF_SKIPDEFAULT); // if we got here, we will paint everything ourself
+				}
+				case OS.CDDS_POSTPAINT: {
+					// get the cursor position
+					POINT cursorPos = new POINT();
+					OS.GetCursorPos(cursorPos);
+					OS.MapWindowPoints(0, hwndHeader, cursorPos, 1);
+
+					// drawing all cells
+					int highlightedHeaderDividerX = -1;
+					int lastColumnRight = -1;
+					RECT [] rects = new RECT [columnCount];
+					for (int i=0; i<columnCount; i++) {
+						rects [i] = new RECT ();
+						OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rects [i]);
+						if (rects[i].right > lastColumnRight) {
+							lastColumnRight = rects[i].right;
+						}
+
+						if (columns[i] == sortColumn && sortDirection != SWT.NONE) {
+							// the display.getSortImage looks terrible after scaling up.
+							long /*int*/ pen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor(OS.COLOR_3DDKSHADOW));
+							long /*int*/ oldPen = OS.SelectObject (nmcd.hdc, pen);
+							int center = rects[i].left + (rects[i].right - rects[i].left) / 2;
+							int leg = 3;
+							if (sortDirection == SWT.UP) {
+								OS.Polyline(nmcd.hdc, new int[] {center-leg, 1+leg, center+1, 0}, 2);
+								OS.Polyline(nmcd.hdc, new int[] {center+leg, 1+leg, center-1, 0}, 2);
+							} else if (sortDirection == SWT.DOWN) {
+								OS.Polyline(nmcd.hdc, new int[] {center-leg, 1, center+1, 1+leg+1}, 2);
+								OS.Polyline(nmcd.hdc, new int[] {center+leg, 1, center-1, 1+leg+1}, 2);
+							}
+							OS.SelectObject (nmcd.hdc, oldPen);
+							OS.DeleteObject (pen);
+						}
+
+						/* Windows 7 and 10 always draw a nearly invisible vertical line between the columns, even if lines are disabled.
+						   This line uses no fixed color constant, but calculates it from the background color.
+						   The method getSlightlyDifferentColor gives us a color, that is near enough to the windows algorithm. */
+						long /*int*/ pen = OS.CreatePen (OS.PS_SOLID, getGridLineWidthInPixels(), getSlightlyDifferentColor(getHeaderBackgroundPixel()));
+						long /*int*/ oldPen = OS.SelectObject (nmcd.hdc, pen);
+						OS.Polyline(nmcd.hdc, new int[] {rects[i].right-1, rects[i].top, rects[i].right-1, rects[i].bottom}, 2);
+						OS.SelectObject (nmcd.hdc, oldPen);
+						OS.DeleteObject (pen);
+
+						if (linesVisible) {
+							pen = OS.CreatePen (OS.PS_SOLID, getGridLineWidthInPixels(), OS.GetSysColor(OS.COLOR_3DFACE));
+							oldPen = OS.SelectObject (nmcd.hdc, pen);
+							OS.Polyline(nmcd.hdc, new int[] {rects[i].right, rects[i].top, rects[i].right, rects[i].bottom}, 2);
+							OS.SelectObject (nmcd.hdc, oldPen);
+							OS.DeleteObject (pen);
+						}
+
+						if (headerItemDragging && highlightedHeaderDividerX == -1) {
+							int distanceToLeftBorder = cursorPos.x - rects[i].left;
+							int distanceToRightBorder = rects[i].right - cursorPos.x;
+							if (distanceToLeftBorder >= 0 && distanceToRightBorder >= 0) {
+								// the cursor is in the current rectangle
+								highlightedHeaderDividerX = distanceToLeftBorder <= distanceToRightBorder ? rects[i].left-1 : rects[i].right;
+							}
+						}
+
+						int x = rects[i].left + INSET + 2;
+						if (columns[i].image != null) {
+							GCData data = new GCData();
+							data.device = display;
+							GC gc = GC.win32_new (nmcd.hdc, data);
+							int y = Math.max (0, (nmcd.bottom - columns[i].image.getBoundsInPixels().height) / 2);
+							gc.drawImage (columns[i].image, DPIUtil.autoScaleDown(x), DPIUtil.autoScaleDown(y));
+							x += columns[i].image.getBoundsInPixels().width + 12;
+							gc.dispose ();
+						}
+
+						if (columns[i].text != null) {
+							int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
+							if ((columns[i].style & SWT.CENTER) != 0) flags |= OS.DT_CENTER;
+							if ((columns[i].style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT;
+							TCHAR buffer = new TCHAR (getCodePage (), columns[i].text, false);
+							OS.SetBkMode(nmcd.hdc, OS.TRANSPARENT);
+							OS.SetTextColor(nmcd.hdc, getHeaderForegroundPixel());
+							RECT textRect = new RECT();
+							textRect.left = x;
+							textRect.top = rects[i].top;
+							textRect.right = rects[i].right;
+							textRect.bottom = rects[i].bottom;
+							OS.DrawText (nmcd.hdc, buffer, buffer.length (), textRect, flags);
+						}
+					}
+
+					if (lastColumnRight < nmcd.right) {
+						// draw background of the 'no column' area
+						RECT rect = new RECT();
+						lastColumnRight += linesVisible ? 1 : 0;
+						OS.SetRect(rect, lastColumnRight, nmcd.top, nmcd.right, nmcd.bottom);
+						long /*int*/ brush = OS.CreateSolidBrush(getHeaderBackgroundPixel());
+						OS.FillRect(nmcd.hdc, rect, brush);
+						OS.DeleteObject(brush);
+					}
+
+					// always draw the highlighted border at the end, to avoid overdrawing by other borders.
+					if (highlightedHeaderDividerX != -1) {
+						long /*int*/ pen = OS.CreatePen (OS.PS_SOLID, 4, OS.GetSysColor(OS.COLOR_HIGHLIGHT));
+						long /*int*/ oldPen = OS.SelectObject (nmcd.hdc, pen);
+						OS.Polyline(nmcd.hdc, new int[] {highlightedHeaderDividerX, nmcd.top, highlightedHeaderDividerX, nmcd.bottom}, 2);
+						OS.SelectObject (nmcd.hdc, oldPen);
+						OS.DeleteObject (pen);
+					}
+
+					return new LRESULT(OS.CDRF_DODEFAULT);
+				}
+			}
+			break;
+		}
 		case OS.NM_RELEASEDCAPTURE: {
 			if (!ignoreColumnMove) {
 				for (int i=0; i<columnCount; i++) {
@@ -7874,10 +8124,12 @@
 					ignoreColumnMove = true;
 					return LRESULT.ONE;
 				}
+				headerItemDragging = true;
 			}
 			break;
 		}
 		case OS.HDN_ENDDRAG: {
+			headerItemDragging = false;
 			NMHEADER phdn = new NMHEADER ();
 			OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
 			if (phdn.iItem != -1 && phdn.pitem != 0) {