Bug 401015 - [CSS] Add support for styling hyperlinks in Links 

Added new API for all platforms:
+ setLinkForeground(Color color) to set the color for
the hyperlink parts of the Link widget.
+ getLinkForeground() returns the link color.

Change-Id: I721a6ef921cdd754a255b5bd8f082bf7170a269d
Signed-off-by: Conrad Groth <info@conrad-groth.de>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java
index 40295d4..f80dff8 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Conrad Groth - Bug 401015 - [CSS] Add support for styling hyperlinks in Links
  *******************************************************************************/
 package org.eclipse.swt.widgets;
 
@@ -43,7 +44,8 @@
 	Point [] offsets;
 	String [] ids;
 	int [] mnemonics;
-	NSColor linkColor;
+	double /*float*/ [] linkForeground;
+	NSColor defaultLinkColor;
 	int focusIndex;
 	boolean ignoreNextMouseUp;
 
@@ -111,35 +113,6 @@
 }
 
 @Override
-boolean textView_clickOnLink_atIndex(long /*int*/ id, long /*int*/ sel, long /*int*/ textView, long /*int*/ link, long /*int*/ charIndex) {
-	NSString str = new NSString (link);
-	Event event = new Event ();
-	event.text = str.getString();
-	sendSelectionEvent (SWT.Selection, event, true);
-	// Widget may be disposed at this point.
-	if (isDisposed()) return true;
-	for (int i = 0; i < offsets.length; i++) {
-		if ((charIndex >= offsets[i].x) && (charIndex <= offsets[i].y)) {
-			focusIndex = i;
-			break;
-		}
-	}
-	redrawWidget(view, false);
-	ignoreNextMouseUp = true;
-	return true;
-}
-
-@Override
-boolean sendMouseEvent (NSEvent nsEvent, int type, boolean send) {
-	if (type == SWT.MouseMove) {
-		if (view.window().firstResponder().id != view.id) {
-			mouseMoved(view.id, OS.sel_mouseMoved_, nsEvent.id);
-		}
-	}
-	return super.sendMouseEvent(nsEvent, type, send);
-}
-
-@Override
 public Point computeSize (int wHint, int hHint, boolean changed) {
 	checkWidget ();
 	if (wHint != SWT.DEFAULT && wHint < 0) wHint = 0;
@@ -227,7 +200,7 @@
 	super.createWidget ();
 	text = "";
 	NSDictionary dict = ((NSTextView)view).linkTextAttributes();
-	linkColor = new NSColor(dict.valueForKey(OS.NSForegroundColorAttributeName));
+	defaultLinkColor = new NSColor(dict.valueForKey(OS.NSForegroundColorAttributeName));
 	offsets = new Point [0];
 	ids = new String [0];
 	mnemonics = new int [0];
@@ -269,6 +242,15 @@
 }
 
 @Override
+void enableWidget (boolean enabled) {
+	super.enableWidget (enabled);
+	NSTextView widget = (NSTextView) view;
+	widget.setTextColor (getTextColor (enabled));
+	setLinkColor (enabled);
+	redrawWidget (view, false);
+}
+
+@Override
 Cursor findCursor () {
 	Cursor cursor = super.findCursor();
 	if (cursor != null) return cursor;
@@ -281,28 +263,32 @@
 	return null;
 }
 
-@Override
-void enableWidget (boolean enabled) {
-	super.enableWidget (enabled);
-	NSColor nsColor = null;
-	if (enabled) {
-		if (foreground == null) {
-			nsColor = NSColor.textColor ();
-		} else {
-			nsColor = NSColor.colorWithDeviceRed (foreground [0], foreground [1], foreground [2], foreground[3]);
-		}
-	} else {
-		nsColor = NSColor.disabledControlTextColor();
+/**
+ * Returns the link foreground color.
+ *
+ * @return the receiver's link 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.105
+ */
+public Color getLinkForeground () {
+	checkWidget ();
+	return Color.cocoa_new (display, display.getNSColorRGB (getLinkForegroundColor ()));
+}
+
+NSColor getLinkForegroundColor () {
+	if (linkForeground != null) {
+		return NSColor.colorWithDeviceRed (linkForeground[0], linkForeground[1], linkForeground[2], linkForeground[3]);
 	}
-	NSTextView widget = (NSTextView)view;
-	widget.setTextColor(nsColor);
-	NSDictionary linkTextAttributes = widget.linkTextAttributes();
-	int count = (int)/*64*/linkTextAttributes.count();
-	NSMutableDictionary dict = NSMutableDictionary.dictionaryWithCapacity(count);
-	dict.setDictionary(linkTextAttributes);
-	dict.setValue(enabled ? linkColor : nsColor, OS.NSForegroundColorAttributeName);
-	widget.setLinkTextAttributes(dict);
-	redrawWidget(view, false);
+	return defaultLinkColor;
+}
+
+@Override
+String getNameText () {
+	return getText ();
 }
 
 NSRect[] getRectangles(int linkIndex) {
@@ -354,12 +340,6 @@
 	return result;
 }
 
-@Override
-String getNameText () {
-	return getText ();
-}
-
-
 /**
  * Returns the receiver's text, which will be an empty
  * string if it has never been set.
@@ -376,6 +356,17 @@
 	return text;
 }
 
+NSColor getTextColor (boolean enabled) {
+	if (enabled) {
+		if (foreground == null) {
+			return NSColor.textColor ();
+		}
+		return NSColor.colorWithDeviceRed (foreground [0], foreground [1], foreground [2], foreground[3]);
+	} else {
+		return NSColor.disabledControlTextColor();
+	}
+}
+
 @Override
 void mouseUp(long /*int*/ id, long /*int*/ sel, long /*int*/ theEvent) {
 	/*
@@ -390,59 +381,6 @@
 	super.mouseUp(id, sel, theEvent);
 }
 
-@Override
-boolean shouldDrawInsertionPoint(long /*int*/ id, long /*int*/ sel) {
-	return false;
-}
-
-@Override
-void register () {
-	super.register ();
-	display.addWidget(scrollView, this);
-}
-
-@Override
-void releaseHandle () {
-	super.releaseHandle ();
-	if (scrollView != null) scrollView.release();
-	scrollView = null;
-}
-
-@Override
-void releaseWidget () {
-	super.releaseWidget ();
-	offsets = null;
-	ids = null;
-	mnemonics = null;
-	text = null;
-	linkColor = null;
-}
-
-/**
- * Removes the listener from the collection of listeners who will
- * be notified when the control is selected by the user.
- *
- * @param listener the listener which should no longer be notified
- *
- * @exception IllegalArgumentException <ul>
- *    <li>ERROR_NULL_ARGUMENT - if the listener is null</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>
- *
- * @see SelectionListener
- * @see #addSelectionListener
- */
-public void removeSelectionListener (SelectionListener listener) {
-	checkWidget ();
-	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
-	if (eventTable == null) return;
-	eventTable.unhook (SWT.Selection, listener);
-	eventTable.unhook (SWT.DefaultSelection, listener);
-}
-
 String parse (String string) {
 	int length = string.length ();
 	offsets = new Point [length / 4];
@@ -593,6 +531,55 @@
 }
 
 @Override
+void register () {
+	super.register ();
+	display.addWidget(scrollView, this);
+}
+
+@Override
+void releaseHandle () {
+	super.releaseHandle ();
+	if (scrollView != null) scrollView.release();
+	scrollView = null;
+}
+
+@Override
+void releaseWidget () {
+	super.releaseWidget ();
+	offsets = null;
+	ids = null;
+	mnemonics = null;
+	text = null;
+	defaultLinkColor = null;
+	linkForeground = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</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>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+	checkWidget ();
+	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+	if (eventTable == null) return;
+	eventTable.unhook (SWT.Selection, listener);
+	eventTable.unhook (SWT.DefaultSelection, listener);
+}
+
+@Override
 void scrollWheel(long /*int*/ id, long /*int*/ sel, long /*int*/ theEvent) {
 	super.scrollWheel(id, sel, theEvent);
 	parent.scrollWheel(parent.view.id, sel, theEvent);
@@ -642,13 +629,13 @@
 }
 
 @Override
-void setBackgroundColor(NSColor nsColor) {
-	setBackground(nsColor);
-}
-
-@Override
-void setBackgroundImage(NSImage image) {
-	((NSTextView) view).setDrawsBackground(image == null);
+boolean sendMouseEvent (NSEvent nsEvent, int type, boolean send) {
+	if (type == SWT.MouseMove) {
+		if (view.window().firstResponder().id != view.id) {
+			mouseMoved(view.id, OS.sel_mouseMoved_, nsEvent.id);
+		}
+	}
+	return super.sendMouseEvent(nsEvent, type, send);
 }
 
 void setBackground(NSColor nsColor) {
@@ -662,6 +649,16 @@
 }
 
 @Override
+void setBackgroundColor(NSColor nsColor) {
+	setBackground(nsColor);
+}
+
+@Override
+void setBackgroundImage(NSImage image) {
+	((NSTextView) view).setDrawsBackground(image == null);
+}
+
+@Override
 void setFont(NSFont font) {
 	((NSTextView) view).setFont(font);
 }
@@ -669,13 +666,49 @@
 @Override
 void setForeground (double /*float*/ [] color) {
 	if (!getEnabled ()) return;
-	NSColor nsColor;
-	if (color == null) {
-		nsColor = NSColor.textColor ();
-	} else {
-		nsColor = NSColor.colorWithDeviceRed (color [0], color [1], color [2], 1);
+	((NSTextView) view).setTextColor (getTextColor (true));
+}
+
+void setLinkColor (boolean enabled) {
+	NSTextView widget = (NSTextView) view;
+	NSDictionary linkTextAttributes = widget.linkTextAttributes ();
+	int count = (int)/*64*/linkTextAttributes.count ();
+	NSMutableDictionary dict = NSMutableDictionary.dictionaryWithCapacity (count);
+	dict.setDictionary (linkTextAttributes);
+	dict.setValue (enabled ? getLinkForegroundColor () : getTextColor (false), OS.NSForegroundColorAttributeName);
+	widget.setLinkTextAttributes (dict);
+}
+
+/**
+ * Sets the link foreground color to the color specified
+ * by the argument, or to the default system color for the link
+ * if the argument is null.
+ * <p>
+ * Note: This operation is a hint and may be overridden by the platform.
+ * </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.105
+ */
+public void setLinkForeground (Color color) {
+	checkWidget ();
+	if (color != null) {
+		if (color.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
 	}
-	((NSTextView) view).setTextColor (nsColor);
+	double /*float*/ [] linkForeground = color != null ? color.handle : null;
+	if (equals (linkForeground, this.linkForeground)) return;
+	this.linkForeground = linkForeground;
+	if (getEnabled ()) {
+		setLinkColor (true);
+	}
+	redrawWidget (view, false);
 }
 
 @Override
@@ -748,6 +781,30 @@
 }
 
 @Override
+boolean shouldDrawInsertionPoint(long /*int*/ id, long /*int*/ sel) {
+	return false;
+}
+
+@Override
+boolean textView_clickOnLink_atIndex(long /*int*/ id, long /*int*/ sel, long /*int*/ textView, long /*int*/ link, long /*int*/ charIndex) {
+	NSString str = new NSString (link);
+	Event event = new Event ();
+	event.text = str.getString();
+	sendSelectionEvent (SWT.Selection, event, true);
+	// Widget may be disposed at this point.
+	if (isDisposed()) return true;
+	for (int i = 0; i < offsets.length; i++) {
+		if ((charIndex >= offsets[i].x) && (charIndex <= offsets[i].y)) {
+			focusIndex = i;
+			break;
+		}
+	}
+	redrawWidget(view, false);
+	ignoreNextMouseUp = true;
+	return true;
+}
+
+@Override
 NSView topView () {
 	return scrollView;
 }
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Link.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Link.java
index d558dea..5172fcf 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Link.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Link.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Conrad Groth - Bug 401015 - [CSS] Add support for styling hyperlinks in Links
  *******************************************************************************/
 package org.eclipse.swt.widgets;
 
@@ -149,7 +150,6 @@
 	OS.gtk_widget_set_has_window (handle, true);
 	OS.gtk_widget_set_can_focus (handle, true);
 	layout = new TextLayout (display);
-	linkColor = display.getSystemColor(SWT.COLOR_LINK_FOREGROUND);
 	disabledColor = new Color (display, LINK_DISABLED_FOREGROUND);
 	offsets = new Point [0];
 	ids = new String [0];
@@ -191,12 +191,7 @@
 void enableWidget (boolean enabled) {
 	super.enableWidget (enabled);
 	if (isDisposed ()) return;
-	TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null);
-	linkStyle.underline = true;
-	for (int i = 0; i < offsets.length; i++) {
-		Point point = offsets [i];
-		layout.setStyle (linkStyle, point.x, point.y);
-	}
+	styleLinkParts();
 	redraw ();
 }
 
@@ -262,6 +257,22 @@
 	});
 }
 
+/**
+ * Returns the link foreground color.
+ *
+ * @return the receiver's link 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.105
+ */
+public Color getLinkForeground () {
+	checkWidget ();
+	return linkColor != null ? linkColor : display.getSystemColor(SWT.COLOR_LINK_FOREGROUND);
+}
+
 @Override
 String getNameText () {
 	return getText ();
@@ -717,6 +728,37 @@
 	layout.setFont (Font.gtk_new (display, font));
 }
 
+/**
+ * Sets the link foreground color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ * <p>
+ * Note: This operation is a hint and may be overridden by the platform.
+ * </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.105
+ */
+public void setLinkForeground (Color color) {
+	checkWidget();
+	if (color != null) {
+		if (color.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
+		if (color.equals(linkColor)) return;
+	} else if (linkColor == null) return;
+	linkColor = color;
+	if (getEnabled()) {
+		styleLinkParts();
+		redraw();
+	}
+}
+
 @Override
 void setOrientation (boolean create) {
     super.setOrientation (create);
@@ -768,13 +810,10 @@
 	layout.setText (parse (string));
 	focusIndex = offsets.length > 0 ? 0 : -1;
 	selection.x = selection.y = -1;
-	boolean enabled = (state & DISABLED) == 0;
-	TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null);
-	linkStyle.underline = true;
+	styleLinkParts();
 	int [] bidiSegments = new int [offsets.length*2];
 	for (int i = 0; i < offsets.length; i++) {
 		Point point = offsets [i];
-		layout.setStyle (linkStyle, point.x, point.y);
 		bidiSegments[i*2] = point.x;
 		bidiSegments[i*2+1] = point.y+1;
 	}
@@ -796,6 +835,16 @@
 	fixStyle (handle);
 }
 
+void styleLinkParts() {
+	boolean enabled = (state & DISABLED) == 0;
+	TextStyle linkStyle = new TextStyle (null, enabled ? getLinkForeground() : disabledColor, null);
+	linkStyle.underline = true;
+	for (int i = 0; i < offsets.length; i++) {
+		Point point = offsets [i];
+		layout.setStyle (linkStyle, point.x, point.y);
+	}
+}
+
 @Override
 int traversalCode (int key, GdkEventKey event) {
 	if (offsets.length == 0) return 0;
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java
index c1b6cd8..e672bd8 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -7,15 +7,16 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Conrad Groth - Bug 401015 - [CSS] Add support for styling hyperlinks in Links
  *******************************************************************************/
 package org.eclipse.swt.widgets;
 
-import org.eclipse.swt.internal.BidiUtil;
-import org.eclipse.swt.internal.win32.*;
 import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.events.*;
 import org.eclipse.swt.accessibility.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+import org.eclipse.swt.internal.win32.*;
 
 /**
  * Instances of this class represent a selectable
@@ -41,19 +42,20 @@
  */
 public class Link extends Control {
 	String text;
-	TextLayout layout;
-	Color linkColor, disabledColor;
+	TextLayout layout; // always track the current layout, even if we don't need it.
+	Color disabledColor;
+	int linkForeground = -1;
 	Point [] offsets;
 	Point selection;
 	String [] ids;
 	int [] mnemonics;
 	int focusIndex, mouseDownIndex;
 	long /*int*/ font;
-	static final RGB LINK_FOREGROUND = new RGB (0, 51, 153);
+	static final RGB LAST_FALLBACK_LINK_FOREGROUND = new RGB (0, 51, 153);
 	static final long /*int*/ LinkProc;
 	static final TCHAR LinkClass = new TCHAR (0, OS.WC_LINK, true);
 	static {
-		if (OS.COMCTL32_MAJOR >= 6) {
+		if (isCommonControlAvailable()) {
 			WNDCLASS lpWndClass = new WNDCLASS ();
 			OS.GetClassInfo (0, LinkClass, lpWndClass);
 			LinkProc = lpWndClass.lpfnWndProc;
@@ -182,7 +184,7 @@
 	if (wHint != SWT.DEFAULT && wHint < 0) wHint = 0;
 	if (hHint != SWT.DEFAULT && hHint < 0) hHint = 0;
 	int width, height;
-	if (OS.COMCTL32_MAJOR >= 6) {
+	if (useCommonControl()) {
 		long /*int*/ hDC = OS.GetDC (handle);
 		long /*int*/ newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
 		long /*int*/ oldFont = OS.SelectObject (hDC, newFont);
@@ -233,32 +235,23 @@
 void createHandle () {
 	super.createHandle ();
 	state |= THEME_BACKGROUND;
-	if (OS.COMCTL32_MAJOR < 6) {
-		layout = new TextLayout (display);
-		if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
-			linkColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_HOTLIGHT));
-		} else {
-			linkColor = new Color (display, LINK_FOREGROUND);
-		}
-		disabledColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_GRAYTEXT));
-		offsets = new Point [0];
-		ids = new String [0];
-		mnemonics = new int [0];
-		selection = new Point (-1, -1);
-		focusIndex = mouseDownIndex = -1;
-	}
+	layout = new TextLayout (display);
+	disabledColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_GRAYTEXT));
+	offsets = new Point [0];
+	ids = new String [0];
+	mnemonics = new int [0];
+	selection = new Point (-1, -1);
+	focusIndex = mouseDownIndex = -1;
 }
 
 @Override
 void createWidget () {
 	super.createWidget ();
 	text = "";
-	if (OS.COMCTL32_MAJOR < 6) {
-		if ((style & SWT.MIRRORED) != 0) {
-			layout.setOrientation (SWT.RIGHT_TO_LEFT);
-		}
-		initAccessible ();
+	if ((style & SWT.MIRRORED) != 0) {
+		layout.setOrientation (SWT.RIGHT_TO_LEFT);
 	}
+	initAccessible ();
 }
 
 void drawWidget (GC gc, RECT rect) {
@@ -294,7 +287,7 @@
 
 @Override
 void enableWidget (boolean enabled) {
-	if (OS.COMCTL32_MAJOR >= 6) {
+	if (useCommonControl(enabled)) {
 		LITEM item = new LITEM ();
 		item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE;
 		item.stateMask = OS.LIS_ENABLED;
@@ -302,14 +295,8 @@
 		while (OS.SendMessage (handle, OS.LM_SETITEM, 0, item) != 0) {
 			item.iLink++;
 		}
-	} else {
-		TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null);
-		linkStyle.underline = true;
-		for (int i = 0; i < offsets.length; i++) {
-			Point point = offsets [i];
-			layout.setStyle (linkStyle, point.x, point.y);
-		}
 	}
+	styleLinkParts(enabled);
 	redraw ();
 	/*
 	* Feature in Windows.  For some reason, setting
@@ -378,6 +365,32 @@
 	});
 }
 
+/**
+ * Returns the link foreground color.
+ *
+ * @return the receiver's link 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.105
+ */
+public Color getLinkForeground () {
+	checkWidget ();
+	return internalGetLinkForeground();
+}
+
+Color internalGetLinkForeground() {
+	if (linkForeground != -1) {
+		return Color.win32_new (display, linkForeground);
+	}
+	if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+		return Color.win32_new (display, OS.GetSysColor (OS.COLOR_HOTLIGHT));
+	}
+	return new Color (display, LAST_FALLBACK_LINK_FOREGROUND);
+}
+
 @Override
 String getNameText () {
 	return getText ();
@@ -438,7 +451,7 @@
 				char mnemonic = parsedText.charAt(mnemonics[i]);
 				if (uckey == Character.toUpperCase (mnemonic)) {
 					if (!setFocus ()) return false;
-					if (OS.COMCTL32_MAJOR >= 6) {
+					if (useCommonControl()) {
 						int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
 						LITEM item = new LITEM ();
 						item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE;
@@ -639,8 +652,6 @@
 	super.releaseWidget ();
 	if (layout != null) layout.dispose ();
 	layout = null;
-	if (linkColor != null) linkColor.dispose ();
-	linkColor = null;
 	disabledColor = null;
 	offsets = null;
 	ids = null;
@@ -674,6 +685,39 @@
 }
 
 /**
+ * Sets the link foreground color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ * <p>
+ * Note: This operation is a hint and may be overridden by the platform.
+ * </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.105
+ */
+public void setLinkForeground (Color color) {
+	checkWidget ();
+	int pixel = -1;
+	if (color != null) {
+		if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+		pixel = color.handle;
+	}
+	if (pixel == linkForeground) return;
+	linkForeground = pixel;
+	if (OS.IsWindowEnabled (handle)) {
+		styleLinkParts(true);
+	}
+	OS.InvalidateRect (handle, null, true);
+}
+
+/**
  * Sets the receiver's text.
  * <p>
  * The string can contain both regular text and hyperlinks.  A hyperlink
@@ -718,8 +762,9 @@
 		updateTextDirection (AUTO_TEXT_DIRECTION);
 	}
 
-	if (OS.COMCTL32_MAJOR >= 6) {
-		boolean enabled = OS.IsWindowEnabled (handle);
+	boolean enabled = OS.IsWindowEnabled (handle);
+	String parsedText = parse (text);
+	if (isCommonControlAvailable()) {
 		/*
 		* Bug in Windows.  For some reason, when SetWindowText()
 		* is used to set the text of a link control to the empty
@@ -729,34 +774,31 @@
 		if (string.length () == 0) string = " ";  //$NON-NLS-1$
 		TCHAR buffer = new TCHAR (getCodePage (), string, true);
 		OS.SetWindowText (handle, buffer);
-		parse (text);
+	}
+
+	layout.setText (parsedText);
+	focusIndex = offsets.length > 0 ? 0 : -1;
+	selection.x = selection.y = -1;
+	int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+	if (offsets.length > 0) {
+		bits |= OS.WS_TABSTOP;
+	} else {
+		bits &= ~OS.WS_TABSTOP;
+	}
+	OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+	styleLinkParts(enabled);
+	TextStyle mnemonicStyle = new TextStyle (null, null, null);
+	mnemonicStyle.underline = true;
+	for (int i = 0; i < mnemonics.length; i++) {
+		int mnemonic  = mnemonics [i];
+		if (mnemonic != -1) {
+			layout.setStyle (mnemonicStyle, mnemonic, mnemonic);
+		}
+	}
+
+	if (useCommonControl()) {
 		enableWidget (enabled);
 	} else {
-		layout.setText (parse (text));
-		focusIndex = offsets.length > 0 ? 0 : -1;
-		selection.x = selection.y = -1;
-		int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
-		if (offsets.length > 0) {
-			bits |= OS.WS_TABSTOP;
-		} else {
-			bits &= ~OS.WS_TABSTOP;
-		}
-		OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
-		boolean enabled = OS.IsWindowEnabled (handle);
-		TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null);
-		linkStyle.underline = true;
-		for (int i = 0; i < offsets.length; i++) {
-			Point point = offsets [i];
-			layout.setStyle (linkStyle, point.x, point.y);
-		}
-		TextStyle mnemonicStyle = new TextStyle (null, null, null);
-		mnemonicStyle.underline = true;
-		for (int i = 0; i < mnemonics.length; i++) {
-			int mnemonic  = mnemonics [i];
-			if (mnemonic != -1) {
-				layout.setStyle (mnemonicStyle, mnemonic, mnemonic);
-			}
-		}
 		redraw ();
 	}
 }
@@ -766,6 +808,15 @@
 	return BidiUtil.resolveTextDirection(text);
 }
 
+void styleLinkParts(boolean enabled) {
+	TextStyle linkStyle = new TextStyle (null, enabled ? internalGetLinkForeground() : disabledColor, null);
+	linkStyle.underline = true;
+	for (int i = 0; i < offsets.length; i++) {
+		Point point = offsets [i];
+		layout.setStyle (linkStyle, point.x, point.y);
+	}
+}
+
 @Override
 boolean updateTextDirection(int textDirection) {
 	if (super.updateTextDirection(textDirection)) {
@@ -780,6 +831,18 @@
 	return false;
 }
 
+boolean useCommonControl() {
+	return useCommonControl(OS.IsWindowEnabled(handle));
+}
+
+boolean useCommonControl(boolean enabled) {
+	return linkForeground == -1 && !enabled && isCommonControlAvailable();
+}
+
+static boolean isCommonControlAvailable() {
+	return OS.COMCTL32_MAJOR >= 6;
+}
+
 @Override
 int widgetStyle () {
 	int bits = super.widgetStyle ();
@@ -788,7 +851,7 @@
 
 @Override
 TCHAR windowClass () {
-	return OS.COMCTL32_MAJOR >= 6 ? LinkClass : display.windowClass;
+	return isCommonControlAvailable() ? LinkClass : display.windowClass;
 }
 
 @Override
@@ -800,7 +863,7 @@
 LRESULT WM_CHAR (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_CHAR (wParam, lParam);
 	if (result != null) return result;
-	if (OS.COMCTL32_MAJOR < 6) {
+	if (!useCommonControl()) {
 		if (focusIndex == -1) return result;
 		switch ((int)/*64*/wParam) {
 			case ' ':
@@ -849,7 +912,7 @@
 	if (result != null) return result;
 	int index, count;
 	long /*int*/ code = 0;
-	if (OS.COMCTL32_MAJOR >= 6) {
+	if (useCommonControl()) {
 		LITEM item = new LITEM ();
 		item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE;
 		item.stateMask = OS.LIS_FOCUSED;
@@ -893,7 +956,7 @@
 LRESULT WM_KEYDOWN (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_KEYDOWN (wParam, lParam);
 	if (result != null) return result;
-	if (OS.COMCTL32_MAJOR >= 6) {
+	if (useCommonControl()) {
 		switch ((int)/*64*/wParam) {
 			case OS.VK_SPACE:
 			case OS.VK_RETURN:
@@ -913,7 +976,7 @@
 @Override
 LRESULT WM_KILLFOCUS (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
-	if (OS.COMCTL32_MAJOR < 6) redraw ();
+	if (!useCommonControl()) redraw ();
 	return result;
 }
 
@@ -921,7 +984,7 @@
 LRESULT WM_LBUTTONDOWN (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
 	if (result == LRESULT.ZERO) return result;
-	if (OS.COMCTL32_MAJOR < 6) {
+	if (!useCommonControl()) {
 		if (focusIndex != -1) setFocus ();
 		int x = OS.GET_X_LPARAM (lParam);
 		int y = OS.GET_Y_LPARAM (lParam);
@@ -960,7 +1023,7 @@
 LRESULT WM_LBUTTONUP (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_LBUTTONUP (wParam, lParam);
 	if (result == LRESULT.ZERO) return result;
-	if (OS.COMCTL32_MAJOR < 6) {
+	if (!useCommonControl()) {
 		if (mouseDownIndex == -1) return result;
 		int x = OS.GET_X_LPARAM (lParam);
 		int y = OS.GET_Y_LPARAM (lParam);
@@ -989,15 +1052,13 @@
 	* returns HTTRANSPARENT when mouse is over plain text. The fix is
 	* to always return HTCLIENT.
 	*/
-	if (OS.COMCTL32_MAJOR >= 6) return new LRESULT (OS.HTCLIENT);
-
-	return result;
+	return new LRESULT (OS.HTCLIENT);
 }
 
 @Override
 LRESULT WM_MOUSEMOVE (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
-	if (OS.COMCTL32_MAJOR < 6) {
+	if (!useCommonControl()) {
 		int x = OS.GET_X_LPARAM (lParam);
 		int y = OS.GET_Y_LPARAM (lParam);
 		if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
@@ -1033,7 +1094,7 @@
 @Override
 LRESULT WM_PAINT (long /*int*/ wParam, long /*int*/ lParam) {
 	if ((state & DISPOSE_SENT) != 0) return LRESULT.ZERO;
-	if (OS.COMCTL32_MAJOR >= 6) {
+	if (useCommonControl()) {
 		return super.WM_PAINT (wParam, lParam);
 	}
 
@@ -1058,7 +1119,7 @@
 @Override
 LRESULT WM_PRINTCLIENT (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
-	if (OS.COMCTL32_MAJOR < 6) {
+	if (!useCommonControl()) {
 		RECT rect = new RECT ();
 		OS.GetClientRect (handle, rect);
 		GCData data = new GCData ();
@@ -1074,15 +1135,13 @@
 @Override
 LRESULT WM_SETFOCUS (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_SETFOCUS (wParam, lParam);
-	if (OS.COMCTL32_MAJOR < 6) redraw ();
+	if (!useCommonControl()) redraw ();
 	return result;
 }
 
 @Override
 LRESULT WM_SETFONT (long /*int*/ wParam, long /*int*/ lParam) {
-	if (OS.COMCTL32_MAJOR < 6) {
-		layout.setFont (Font.win32_new (display, wParam));
-	}
+	layout.setFont (Font.win32_new (display, wParam));
 	if (lParam != 0) OS.InvalidateRect (handle, null, true);
 	return super.WM_SETFONT (font = wParam, lParam);
 }
@@ -1090,10 +1149,10 @@
 @Override
 LRESULT WM_SIZE (long /*int*/ wParam, long /*int*/ lParam) {
 	LRESULT result = super.WM_SIZE (wParam, lParam);
-	if (OS.COMCTL32_MAJOR < 6) {
-		RECT rect = new RECT ();
-		OS.GetClientRect (handle, rect);
-		layout.setWidth (rect.right > 0 ? rect.right : -1);
+	RECT rect = new RECT ();
+	OS.GetClientRect (handle, rect);
+	layout.setWidth (rect.right > 0 ? rect.right : -1);
+	if (!useCommonControl()) {
 		redraw ();
 	}
 	return result;
@@ -1107,7 +1166,7 @@
 	* not gray out the non-link portion of the text.  The fix
 	* is to set the text color to the system gray color.
 	*/
-	if (OS.COMCTL32_MAJOR >= 6) {
+	if (useCommonControl()) {
 		if (!OS.IsWindowEnabled (handle)) {
 			OS.SetTextColor (wParam, OS.GetSysColor (OS.COLOR_GRAYTEXT));
 			if (result == null) {
@@ -1123,7 +1182,7 @@
 
 @Override
 LRESULT wmNotifyChild (NMHDR hdr, long /*int*/ wParam, long /*int*/ lParam) {
-	if (OS.COMCTL32_MAJOR >= 6) {
+	if (useCommonControl()) {
 		switch (hdr.code) {
 			case OS.NM_RETURN:
 			case OS.NM_CLICK:
diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java
index 48246d6..4c2c9f6 100644
--- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java
+++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 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
@@ -12,12 +12,14 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Link;
 import org.junit.Before;
@@ -152,4 +154,15 @@
 	} catch (IllegalArgumentException e) {
 	}
 }
+
+@Test
+public void test_setLinkForegroundLorg_eclipse_swt_graphics_Color() {
+	assertNotNull(link.getLinkForeground());
+	Color color = new Color(control.getDisplay(), 12, 34, 56);
+	link.setLinkForeground(color);
+	assertEquals(color, link.getLinkForeground());
+	link.setLinkForeground(null);
+	assertFalse(link.getForeground().equals(color));
+	color.dispose();
+}
 }