Bug 565679 - [Win32] SWT requests WS_EX_CLIENTEDGE and then tries to make it look like WS_BORDER

This patch addresses problems in Tree:

* Border type is now `WS_BORDER` instead if incorrect `WS_EX_CLIENTEDGE`
  `WS_BORDER` is a flat 1px border.
  `WS_EX_CLIENTEDGE` is a 3D 2px border.
  Workarounds forced Tree to appear as if it had `WS_BORDER`.

* When `hwndParent` is created, border is removed on Tree and added to
  `hwndParent` instead. This is more reasonable and allows to remove
  workaround in `Tree.setScrollWidth()` which moved unwanted borders
  outside the visible area.

Side effects:

* Tree contents moved one pixel left/up, because border is now 1px
  thinner. I think that this is fine.

* Border is now darker, matching original Windows appearance for
  borders. I consider this to be a good change, because it brings SWT
  in line with native applications.

Change-Id: I551cd86d746392b625dfd5cc70130fb89b1d0ca5
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java
index eeefc7d..01a3267 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java
@@ -1219,7 +1219,19 @@
 	if ((bits1 & OS.WS_EX_CLIENTEDGE) != 0) return OS.GetSystemMetrics (OS.SM_CXEDGE);
 	if ((bits1 & OS.WS_EX_STATICEDGE) != 0) return OS.GetSystemMetrics (OS.SM_CXBORDER);
 	int bits2 = OS.GetWindowLong (borderHandle, OS.GWL_STYLE);
-	if ((bits2 & OS.WS_BORDER) != 0) return OS.GetSystemMetrics (OS.SM_CXBORDER);
+
+	if ((bits2 & OS.WS_BORDER) != 0) {
+		/*
+		 * For compatibility reasons, isUseWsBorder() shall not change layout size
+		 * compared to previously used WS_EX_CLIENTEDGE. Removing this workaround
+		 * saves screen space, but could break some layouts.
+		 */
+		if (isUseWsBorder ())
+			return OS.GetSystemMetrics (OS.SM_CXEDGE);
+
+		return OS.GetSystemMetrics (OS.SM_CXBORDER);
+	}
+
 	return 0;
 }
 
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java
index a219968..57e86dd 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java
@@ -219,8 +219,6 @@
 	boolean useWsBorderTable = false;
 	static final String USE_WS_BORDER_TEXT_KEY       = "org.eclipse.swt.internal.win32.Text.use_WS_BORDER"; //$NON-NLS-1$
 	boolean useWsBorderText = false;
-	static final String USE_WS_BORDER_TREE_KEY       = "org.eclipse.swt.internal.win32.Tree.use_WS_BORDER"; //$NON-NLS-1$
-	boolean useWsBorderTree = false;
 	/**
 	 * Changes the color of Table header's column delimiters.
 	 * Only affects custom-drawn header, that is when background/foreground header color is set.
@@ -4416,9 +4414,6 @@
 		case USE_WS_BORDER_TEXT_KEY:
 			useWsBorderText    = !disableCustomThemeTweaks && _toBoolean(value);
 			return;
-		case USE_WS_BORDER_TREE_KEY:
-			useWsBorderTree    = !disableCustomThemeTweaks && _toBoolean(value);
-			return;
 		case TABLE_HEADER_LINE_COLOR_KEY:
 			tableHeaderLinePixel = disableCustomThemeTweaks ? -1 : _toColorPixel(value);
 			return;
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 12d1258..da9f969 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
@@ -2208,15 +2208,36 @@
 	OS.SendMessage (itemToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
 }
 
+/**
+ * On Windows, Tree does not support columns. The workaround is to emulate it
+ * by adding a Header control and custom-drawing Tree items.
+ *
+ * Creates Header (for columns) and wraps (Tree+Header) into an intermediate
+ * parent, so that (Tree+Header) behave as one whole. The wrapper is designed
+ * to mimic original Tree as much as possible. For that reason, all sorts of
+ * settings are copied over.
+ */
 void createParent () {
 	forceResize ();
+
+	/* Copy Tree position to hwndParent */
 	RECT rect = new RECT ();
 	OS.GetWindowRect (handle, rect);
 	OS.MapWindowPoints (0, parent.handle, rect, 2);
-	int oldStyle = OS.GetWindowLong (handle, OS.GWL_STYLE);
-	int newStyle = super.widgetStyle () & ~OS.WS_VISIBLE;
+
+	/* Copy Tree styles to hwndParent */
+	final int oldStyle = OS.GetWindowLong (handle, OS.GWL_STYLE);
+	int newStyle = super.widgetStyle ();
+	newStyle &= ~OS.WS_VISIBLE;	/* Show control once everything is configured */
 	if ((oldStyle & OS.WS_DISABLED) != 0) newStyle |= OS.WS_DISABLED;
-//	if ((oldStyle & OS.WS_VISIBLE) != 0) newStyle |= OS.WS_VISIBLE;
+
+	/* Get rid of internal borders; hwndParent will have the borders now */
+	if ((oldStyle & OS.WS_BORDER) != 0) {
+		int noBorderStyle = oldStyle & ~OS.WS_BORDER;
+		OS.SetWindowLong (handle, OS.GWL_STYLE, noBorderStyle);
+	}
+
+	/* Create hwndParent */
 	hwndParent = OS.CreateWindowEx (
 		super.widgetExtStyle (),
 		super.windowClass (),
@@ -2231,8 +2252,14 @@
 		OS.GetModuleHandle (null),
 		null);
 	if (hwndParent == 0) error (SWT.ERROR_NO_HANDLES);
+
+	/* Old code, not sure if needed */
 	OS.SetWindowLongPtr (hwndParent, OS.GWLP_ID, hwndParent);
+
+	/* Copy dark scrollbar settings to hwndParent */
 	OS.SetWindowTheme (hwndParent, display.getExplorerTheme(), null);
+
+	/* Create header */
 	int bits = OS.WS_EX_NOINHERITLAYOUT;
 	if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL;
 	hwndHeader = OS.CreateWindowEx (
@@ -2246,12 +2273,20 @@
 		OS.GetModuleHandle (null),
 		null);
 	if (hwndHeader == 0) error (SWT.ERROR_NO_HANDLES);
+
+	/* Old code, not sure if needed */
 	OS.SetWindowLongPtr (hwndHeader, OS.GWLP_ID, hwndHeader);
+
+	/* Copy Tree's font to header */
 	long hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
 	if (hFont != 0) OS.SendMessage (hwndHeader, OS.WM_SETFONT, hFont, 0);
+
+	/* Copy Tree's tab-order to hwndParent */
 	long hwndInsertAfter = OS.GetWindow (handle, OS.GW_HWNDPREV);
 	int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
 	OS.SetWindowPos (hwndParent, hwndInsertAfter, 0, 0, 0, 0, flags);
+
+	/* Copy Tree's scrollbar settings to hwndParent */
 	SCROLLINFO info = new SCROLLINFO ();
 	info.cbSize = SCROLLINFO.sizeof;
 	info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
@@ -2261,9 +2296,13 @@
 	OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
 	info.nPage = info.nMax + 1;
 	OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
+
+	/* Columns are emulated by custom drawing items */
 	customDraw = true;
+
 	deregister ();
 	if ((oldStyle & OS.WS_VISIBLE) != 0) {
+		/* All set, show the new hwndParent wrapper */
 		OS.ShowWindow (hwndParent, OS.SW_SHOW);
 	}
 	long hwndFocus = OS.GetFocus ();
@@ -3793,7 +3832,7 @@
 
 @Override
 boolean isUseWsBorder () {
-	return super.isUseWsBorder () || ((display != null) && display.useWsBorderTree);
+	return true;
 }
 
 void redrawSelection () {
@@ -4851,13 +4890,11 @@
 	if (playout.prc != 0) OS.HeapFree (hHeap, 0, playout.prc);
 	if (playout.pwpos != 0) OS.HeapFree (hHeap, 0, playout.pwpos);
 	OS.SetWindowPos (hwndHeader, OS.HWND_TOP, pos.x - left, pos.y, pos.cx + left, pos.cy, OS.SWP_NOACTIVATE);
-	int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
-	int b = (bits & OS.WS_EX_CLIENTEDGE) != 0 ? OS.GetSystemMetrics (OS.SM_CXEDGE) : 0;
 	int w = pos.cx + (columnCount == 0 && width == 0 ? 0 : OS.GetSystemMetrics (OS.SM_CXVSCROLL));
 	int h = rect.bottom - rect.top - pos.cy;
 	boolean oldIgnore = ignoreResize;
 	ignoreResize = true;
-	OS.SetWindowPos (handle, 0, pos.x - left - b, pos.y + pos.cy - b, w + left + b * 2, h + b * 2, OS.SWP_NOACTIVATE | OS.SWP_NOZORDER);
+	OS.SetWindowPos (handle, 0, pos.x - left, pos.y + pos.cy, w + left, h, OS.SWP_NOACTIVATE | OS.SWP_NOZORDER);
 	ignoreResize = oldIgnore;
 }
 
diff --git a/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug565679_WS_BORDER.java b/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug565679_WS_BORDER.java
index 224f0c9..cbe0804 100644
--- a/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug565679_WS_BORDER.java
+++ b/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug565679_WS_BORDER.java
@@ -52,10 +52,28 @@
 			new Spinner(compControls, 0);
 
 			new Label(compControls, 0).setText("Tree (with header)");
-			Tree tree = new Tree(compControls, SWT.BORDER);
-			tree.setHeaderVisible(true);
-			new TreeColumn(tree, 0).setText("Column");
-			new TreeItem(tree, 0).setText("Item");
+			{
+				Tree tree = new Tree(compControls, SWT.BORDER);
+				tree.setHeaderVisible(true);
+				TreeColumn column = new TreeColumn(tree, 0);
+				column.setText("Column");
+				column.setWidth(100);
+				TreeItem treeItem1 = new TreeItem(tree, 0);
+				treeItem1.setText("Item");
+				TreeItem treeItem2 = new TreeItem(treeItem1, 0);
+				treeItem2.setText("Item");
+				treeItem1.setExpanded(true);
+			}
+
+			new Label(compControls, 0).setText("Tree (no header)");
+			{
+				Tree tree = new Tree(compControls, SWT.BORDER);
+				TreeItem treeItem1 = new TreeItem(tree, 0);
+				treeItem1.setText("Item");
+				TreeItem treeItem2 = new TreeItem(treeItem1, 0);
+				treeItem2.setText("Item");
+				treeItem1.setExpanded(true);
+			}
 		}
 
 		new Label(shell, 0).setText(
@@ -91,9 +109,6 @@
 			new Label(compControls, 0).setText("ToolBar");
 			ToolBar toolbar = new ToolBar(compControls, SWT.BORDER);
 			toolbar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-			new Label(compControls, 0).setText("Tree (no header)");
-			new Tree(compControls, SWT.BORDER);
 		}
 
 		new Label(shell, 0).setText(
@@ -115,7 +130,7 @@
 			new TabItem(tabFolder, 0).setText("Item");
 		}
 
-		shell.setSize(400, 800);
+		shell.setSize(400, 900);
 		shell.open();
 
 		while (!shell.isDisposed()) {