Bug 11620 - provide support for links (including special support for installer page link) in description column of remote connections view
authordadubrow
Thu, 16 Sep 2010 09:17:01 -0500
changeset 2028 ff6b4aae1c1e
parent 2027 2a1cbc6a1c85
child 2029 fc0ca46d10e2
Bug 11620 - provide support for links (including special support for installer page link) in description column of remote connections view
connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/internal/ui/ConnectionUIUtils.java
connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/messages.properties
connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/settings/ui/ConnectionSettingsPage.java
connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/settings/ui/SettingsWizard.java
connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/view/ConnectionsView.java
connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/view/DescriptionLabelProvider.java
--- a/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/internal/ui/ConnectionUIUtils.java	Thu Sep 16 09:07:54 2010 -0500
+++ b/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/internal/ui/ConnectionUIUtils.java	Thu Sep 16 09:17:01 2010 -0500
@@ -20,6 +20,7 @@
 import java.util.Collection;
 
 import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceColors;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.widgets.Display;
@@ -76,6 +77,7 @@
 	public static final Color COLOR_GREEN = new Color(Display.getDefault(), 0, 128, 0);
 	public static final Color COLOR_ELECTRIC = new Color(Display.getDefault(), 0, 0, 255);
 	public static final Color COLOR_GREY = new Color(Display.getDefault(), 96, 96, 96);
+	public static final Color COLOR_HYPERLINK = JFaceColors.getHyperlinkText(Display.getDefault());
 	
 	/**
 	 * Get the image representing the connection status.
--- a/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/messages.properties	Thu Sep 16 09:07:54 2010 -0500
+++ b/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/messages.properties	Thu Sep 16 09:17:01 2010 -0500
@@ -67,7 +67,6 @@
 ConnectionsView.ToggleServicesLabel=Toggle periodic service testing
 ConnectionsView.TypeColumnHeader=Type
 ConnectionsView.InUseLabel=In use
-ConnectionsView.InUseDesc=At least one service is using this connection
 ConnectionsView.DisconnectedLabel=Disconnected
 ConnectionsView.DisconnectedDesc=Connection has been disconnected while in use, please reconnect
 ConnectionTypePage.ConnectionNameInUseError=The connection name ''{0}'' is in use
@@ -87,6 +86,7 @@
 ConnectionStatusSelectorContribution.NotInUse=Not in use
 ConnectionStatusSelectorContribution.StatusFormat=Connection is {0}: {1}
 ConnectionStatusSelectorContribution.TestServicesMenuLabel=Test services
+DescriptionLabelProvider_InUseDesc=At least one service is using this connection
 DeviceDiscoveryPrequisiteErrorDialog_Description=At least one device discovery agent had load errors that prevent it from discovering connections to devices. Select one to get more information about its error.
 DeviceDiscoveryPrequisiteErrorDialog_DontAskAgainLabel=Don't ask again
 DeviceDiscoveryPrequisiteErrorDialog_DontAskAgainToolTipText=Check this to ignore further discovery agent load errors
--- a/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/settings/ui/ConnectionSettingsPage.java	Thu Sep 16 09:07:54 2010 -0500
+++ b/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/settings/ui/ConnectionSettingsPage.java	Thu Sep 16 09:17:01 2010 -0500
@@ -189,6 +189,9 @@
 		createSetupTabComposite(tabFolder);
 		createAgentTestTabComposite(tabFolder);
 		createInstallTabComposite(tabFolder);
+		int index = settingsWizard.getSelectedTabIndexInConnectionSettingsPage();
+		if (index >= 0 && index < 3)
+			tabFolder.setSelection(index);
 		
 		RemoteConnectionsActivator.setHelp(tabFolder, ".connection_settings_page"); //$NON-NLS-1$
 		
--- a/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/settings/ui/SettingsWizard.java	Thu Sep 16 09:07:54 2010 -0500
+++ b/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/settings/ui/SettingsWizard.java	Thu Sep 16 09:17:01 2010 -0500
@@ -45,6 +45,7 @@
 	private IConnection connectionToEdit;
 	private IService serviceToRestrict;
 	private Map<IConnectedService, Boolean> savedEnabledStates;
+	private int selectedTabIndexInConnectionSettingsPage;
 	
 	public SettingsWizard(IConnection connectionToEdit, IService serviceToRestrict) {
 		this.connectionToEdit = connectionToEdit;
@@ -162,4 +163,16 @@
 		connectionSettingsPage.updateDynamicUI();
 		getContainer().updateButtons();
 	}
+	
+	/**
+	 * Call before opening the wizard to set which tab will be selected in the setting page
+	 * @param index int
+	 */
+	public void setSelectedTabInSettingsPage(int index) {
+		this.selectedTabIndexInConnectionSettingsPage = index;
+	}
+	
+	int getSelectedTabIndexInConnectionSettingsPage() {
+		return selectedTabIndexInConnectionSettingsPage;
+	}
 }
--- a/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/view/ConnectionsView.java	Thu Sep 16 09:07:54 2010 -0500
+++ b/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/view/ConnectionsView.java	Thu Sep 16 09:17:01 2010 -0500
@@ -89,16 +89,15 @@
 import com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionsManagerListener;
 import com.nokia.carbide.remoteconnections.internal.ToggleDiscoveryAgentAction;
 import com.nokia.carbide.remoteconnections.internal.api.IConnection2;
-import com.nokia.carbide.remoteconnections.internal.api.IToggleServicesTestingListener;
 import com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatus;
 import com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatus.EConnectionStatus;
 import com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatusChangedListener;
 import com.nokia.carbide.remoteconnections.internal.api.IDeviceDiscoveryAgent;
+import com.nokia.carbide.remoteconnections.internal.api.IToggleServicesTestingListener;
 import com.nokia.carbide.remoteconnections.internal.registry.Registry;
 import com.nokia.carbide.remoteconnections.internal.ui.ConnectionUIUtils;
 import com.nokia.carbide.remoteconnections.settings.ui.SettingsWizard;
 import com.nokia.cpp.internal.api.utils.core.ObjectUtils;
-import com.nokia.cpp.internal.api.utils.core.TextUtils;
 
 
 /**
@@ -119,7 +118,7 @@
 	private List<Action> serviceSelectedActions;
 	private List<Action> discoveryAgentActions;
 	private static final String UID = ".uid"; //$NON-NLS-1$
-
+	
 	private static final ImageDescriptor CONNECTION_NEW_IMGDESC = RemoteConnectionsActivator.getImageDescriptor("icons/connectionNew.png"); //$NON-NLS-1$
 	private static final ImageDescriptor CONNECTION_EDIT_IMGDESC = RemoteConnectionsActivator.getImageDescriptor("icons/connectionEdit.png"); //$NON-NLS-1$
 	private static final ImageDescriptor SERVICE_TEST_IMGDESC = RemoteConnectionsActivator.getImageDescriptor("icons/serviceTest.png"); //$NON-NLS-1$
@@ -265,9 +264,6 @@
 			return getNodeDisplayName(obj);
 		}
 		
-		/* (non-Javadoc)
-		 * @see org.eclipse.jface.viewers.ColumnLabelProvider#getFont(java.lang.Object)
-		 */
 		@Override
 		public Font getFont(Object element) {
 			if (element instanceof TreeNode) {
@@ -345,9 +341,6 @@
 			return null;
 		}
 		
-		/* (non-Javadoc)
-		 * @see org.eclipse.jface.viewers.ColumnLabelProvider#getFont(java.lang.Object)
-		 */
 		@Override
 		public Font getFont(Object element) {
 			// we need this to avoid letting the bold name column influence the others 
@@ -355,46 +348,6 @@
 		}
 	}
 	
-	public class DescriptionLabelProvider extends ColumnLabelProvider {
-		
-		@Override
-		public String getText(Object obj) {
-			TreeNode node = (TreeNode) obj;
-			Object value = node.getValue();
-			if (value instanceof IConnectedService) {
-				IStatus status = ((IConnectedService) value).getStatus();
-				IConnection connection = findConnection((IConnectedService) value);
-				if (!status.getEStatus().equals(EStatus.IN_USE) ||
-						!(connection != null && ConnectionUIUtils.isSomeServiceInUse(connection))) { // if in-use, we show it in the connection row
-					String longDescription = status.getLongDescription();
-					if (longDescription != null)
-						longDescription = TextUtils.canonicalizeNewlines(longDescription, " "); //$NON-NLS-1$
-					return longDescription;
-				}
-			}
-			else if (value instanceof IConnection) {
-				IConnectionStatus status = getConnectionStatus((IConnection) value);
-				if (status != null) {
-					return status.getLongDescription();
-				}
-				else if (ConnectionUIUtils.isSomeServiceInUse((IConnection) value)) {
-					return Messages.getString("ConnectionsView.InUseDesc"); //$NON-NLS-1$
-				}
-			}
-			
-			return null;
-		}
-		
-		/* (non-Javadoc)
-		 * @see org.eclipse.jface.viewers.ColumnLabelProvider#getFont(java.lang.Object)
-		 */
-		@Override
-		public Font getFont(Object element) {
-			// we need this to avoid letting the bold name column influence the others 
-			return JFaceResources.getDefaultFont();
-		}
-	}
-
 	private class TypeLabelProvider extends ColumnLabelProvider {
 		
 		public String getText(Object obj) {
@@ -406,9 +359,6 @@
 			return null;
 		}
 		
-		/* (non-Javadoc)
-		 * @see org.eclipse.jface.viewers.ColumnLabelProvider#getFont(java.lang.Object)
-		 */
 		@Override
 		public Font getFont(Object element) {
 			// we need this to avoid letting the bold name column influence the others 
@@ -466,9 +416,6 @@
 		}
 	}
 
-	/* (non-Javadoc)
-	 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
-	 */
 	public void createPartControl(Composite parent) {
 		viewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
 		
@@ -495,7 +442,7 @@
 		statusColumn.getColumn().setText(Messages.getString("ConnectionsView.StatusColumnHeader")); //$NON-NLS-1$
 		
 		TreeViewerColumn descriptionColumn = new TreeViewerColumn(viewer, SWT.LEFT);
-		descriptionColumn.setLabelProvider(new DescriptionLabelProvider());
+		descriptionColumn.setLabelProvider(new DescriptionLabelProvider(this, descriptionColumn));
 		descriptionColumn.getColumn().setText(Messages.getString("ConnectionsView.DescriptionColumnHeader")); //$NON-NLS-1$
 		
 		viewer.setContentProvider(new TreeNodeContentProvider());
@@ -601,7 +548,7 @@
 	}
 
 	// returns non-null status when status is not EConnectionStatus.NONE
-	private IConnectionStatus getConnectionStatus(IConnection connection) {
+	public IConnectionStatus getConnectionStatus(IConnection connection) {
 		if (connection instanceof IConnection2) {
 			IConnectionStatus status = ((IConnection2) connection).getStatus();
 			if (status != null && status.getEConnectionStatus() != EConnectionStatus.NONE)
@@ -973,7 +920,7 @@
 		super.dispose();
 	}
 	
-	private static IConnection findConnection(IConnectedService cs) {
+	public static IConnection findConnection(IConnectedService cs) {
 		for (IConnection connection : Registry.instance().getConnections()) {
 			for (IConnectedService connectedService : Registry.instance().getConnectedServices(connection)) {
 				if (cs.equals(connectedService))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/view/DescriptionLabelProvider.java	Thu Sep 16 09:17:01 2010 -0500
@@ -0,0 +1,285 @@
+package com.nokia.carbide.remoteconnections.view;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.StyledString.Styler;
+import org.eclipse.jface.viewers.TreeNode;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.TextStyle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+
+import com.nokia.carbide.remoteconnections.Messages;
+import com.nokia.carbide.remoteconnections.interfaces.IConnectedService;
+import com.nokia.carbide.remoteconnections.interfaces.IConnectedService.IStatus;
+import com.nokia.carbide.remoteconnections.interfaces.IConnectedService.IStatus.EStatus;
+import com.nokia.carbide.remoteconnections.interfaces.IConnection;
+import com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatus;
+import com.nokia.carbide.remoteconnections.internal.ui.ConnectionUIUtils;
+import com.nokia.carbide.remoteconnections.settings.ui.SettingsWizard;
+import com.nokia.cpp.internal.api.utils.core.TextUtils;
+import com.nokia.cpp.internal.api.utils.ui.WorkbenchUtils;
+
+public class DescriptionLabelProvider extends StyledCellLabelProvider {
+
+	private class Element {
+		private String text;
+
+		public Element(String text) {
+			this.text = text;
+		}
+		
+		public String getText() {
+			return text;
+		}
+	}
+	
+	private class LinkElement extends Element {
+		private String href;
+		
+		public LinkElement(String href, String text) {
+			super(text);
+			this.href = href;
+		}
+		
+		public String getHref() {
+			return href;
+		}
+	}
+	
+	private static final Pattern HREF_PATTERN = 
+		Pattern.compile("<a href=\"([^\"]*)\">(.*?)</a>", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
+	private static final String AGENT_INSTALLERS_URL = "about:agentInstallers"; //$NON-NLS-1$
+
+	private final ConnectionsView connectionsView;
+	private Styler hyperLinkStyler;
+	private TreeViewerColumn treeViewerColumn;
+	private Listener mouseListener;
+
+	public DescriptionLabelProvider(ConnectionsView connectionsView, TreeViewerColumn treeViewerColumn) {
+		this.connectionsView = connectionsView;
+		this.treeViewerColumn = treeViewerColumn;
+		hookColumn();
+		hyperLinkStyler = new Styler() {
+			@Override
+			public void applyStyles(TextStyle textStyle) {
+				textStyle.foreground = ConnectionUIUtils.COLOR_HYPERLINK;
+				textStyle.underline = true;
+			}
+		};
+	}
+	
+	private void hookColumn() {
+		final Control control = treeViewerColumn.getViewer().getControl();
+		final Cursor originalCursor = control.getCursor();
+		final Cursor handCursor = treeViewerColumn.getColumn().getDisplay().getSystemCursor(SWT.CURSOR_HAND);
+		mouseListener = new Listener() {
+			public void handleEvent(Event event) {
+				switch (event.type) {
+					case SWT.MouseEnter:
+					case SWT.MouseMove:
+						if (getLinkHRef(event) != null) {
+							control.setCursor(handCursor);
+						}
+						else {
+							control.setCursor(originalCursor);
+						}
+						break;
+					case SWT.MouseExit:
+						control.setCursor(originalCursor);
+						break;
+					case SWT.MouseDown:
+						String linkHRef = getLinkHRef(event);
+						handleLinkClicked(linkHRef, event);
+						break;
+					default:
+				}
+			}
+
+			private String getLinkHRef(Event event) {
+				Point pt = new Point(50, event.y);
+				ViewerCell cell = getViewer().getCell(pt);
+				if (cell == null)
+					return null;
+				TreeItem item = (TreeItem) cell.getItem();
+				String text = getText(cell.getElement());
+				List<Element> elements = parseText(text);
+				if (elements.isEmpty())
+					return null;
+				int locMouseX = event.x - item.getTextBounds(3).x;
+				GC gc = new GC(item.getDisplay());
+				try {
+					gc.setFont(item.getFont());
+					int elementStartX = 0;
+					int elementEndX = 0;
+					for (Element element : elements) {
+						elementEndX += gc.stringExtent(element.getText()).x;
+						if (element instanceof LinkElement) {
+							if (locMouseX >= elementStartX && locMouseX < elementEndX) {
+								LinkElement linkElement = (LinkElement) element;
+								return linkElement.getHref();
+							}
+						}
+						elementStartX = elementEndX + 1;
+					}
+				}
+				finally {
+					gc.dispose();
+				}
+				return null;
+			}
+			
+			private void handleLinkClicked(final String linkHRef, final Event event) {
+				BusyIndicator.showWhile(Display.getDefault(), new Runnable() {
+					public void run() {
+						if (linkHRef.equalsIgnoreCase(AGENT_INSTALLERS_URL)) {
+							Point pt = new Point(50, event.y);
+							ViewerCell cell = getViewer().getCell(pt);
+							if (cell == null)
+								return;
+							TreeItem treeItem = findParentTreeItem((TreeItem) cell.getItem());
+							TreeNode node = (TreeNode) treeItem.getData();
+							final Object value = node.getValue();
+							if (value instanceof IConnection) {
+								Display.getDefault().asyncExec(new Runnable() {
+									public void run() {
+										SettingsWizard wizard = new SettingsWizard((IConnection) value);
+										wizard.setSelectedTabInSettingsPage(2);
+										wizard.open(connectionsView.getViewSite().getShell());
+									}
+								});
+							}
+						}
+						else {
+							try {
+								URL url = new URL(linkHRef);
+								WorkbenchUtils.showWebPageInExternalBrowser(url.toExternalForm());
+							} catch (MalformedURLException e) {
+							}
+						}
+					}
+				});
+			}
+
+			private TreeItem findParentTreeItem(TreeItem item) {
+				TreeItem parentItem = item.getParentItem();
+				while (parentItem != null) {
+					item = parentItem;
+					parentItem = item.getParentItem();
+				}
+				return item;
+			}
+
+		};
+		Widget widget = treeViewerColumn.getViewer().getControl();
+		widget.addListener(SWT.MouseEnter, mouseListener);
+		widget.addListener(SWT.MouseMove, mouseListener);
+		widget.addListener(SWT.MouseExit, mouseListener);
+		widget.addListener(SWT.MouseDown, mouseListener);
+	}
+	
+	private void unhookColumn() {
+		Widget widget = treeViewerColumn.getViewer().getControl();
+		if (widget == null || widget.isDisposed())
+			return;
+		widget.removeListener(SWT.MouseEnter, mouseListener);
+		widget.removeListener(SWT.MouseMove, mouseListener);
+		widget.removeListener(SWT.MouseExit, mouseListener);
+		widget.removeListener(SWT.MouseDown, mouseListener);
+	}
+
+	@Override
+	public void update(ViewerCell cell) {
+		Object element = cell.getElement();
+		String text = getText(element);
+		List<Element> elements = parseText(text);
+		
+		StyledString styledString = getStyledString(elements);
+		cell.setText(styledString.toString());
+		cell.setStyleRanges(styledString.getStyleRanges());
+
+		// we need this to avoid letting the bold name column influence the others 
+		cell.setFont(JFaceResources.getDefaultFont());
+
+		super.update(cell);
+	}
+	
+	private List<Element> parseText(String text) {
+		List<Element> elements = new ArrayList<Element>();
+		if (text != null) {
+			Matcher m = HREF_PATTERN.matcher(text);
+			int start = 0;
+			int end;
+			while (m.find()) {
+				end = m.start();
+				if (start <= end)
+					elements.add(new Element(text.substring(start, end)));
+				elements.add(new LinkElement(m.group(1), m.group(2)));
+				start = m.end();
+			}
+			end = text.length();
+			if (start <= end)
+				elements.add(new Element(text.substring(start, end)));
+		}
+		return elements;
+	}
+
+	private StyledString getStyledString(List<Element> elements) {
+		StyledString styledString = new StyledString();
+		for (Element element : elements) {
+			styledString.append(element.getText(), element instanceof LinkElement ? hyperLinkStyler : null);
+		}
+		return styledString;
+	}
+
+	private String getText(Object obj) {
+		TreeNode node = (TreeNode) obj;
+		Object value = node.getValue();
+		if (value instanceof IConnectedService) {
+			IStatus status = ((IConnectedService) value).getStatus();
+			IConnection connection = ConnectionsView.findConnection((IConnectedService) value);
+			if (!status.getEStatus().equals(EStatus.IN_USE) ||
+					!(connection != null && ConnectionUIUtils.isSomeServiceInUse(connection))) { // if in-use, we show it in the connection row
+				String longDescription = status.getLongDescription();
+				if (longDescription != null)
+					longDescription = TextUtils.canonicalizeNewlines(longDescription, " "); //$NON-NLS-1$
+				return longDescription;
+			}
+		}
+		else if (value instanceof IConnection) {
+			IConnectionStatus status = this.connectionsView.getConnectionStatus((IConnection) value);
+			if (status != null) {
+				return status.getLongDescription();
+			}
+			else if (ConnectionUIUtils.isSomeServiceInUse((IConnection) value)) {
+				return Messages.getString("DescriptionLabelProvider_InUseDesc"); //$NON-NLS-1$
+			}
+		}
+		
+		return null;
+	}
+	
+	@Override
+	public void dispose() {
+		unhookColumn();
+		super.dispose();
+	}
+}
\ No newline at end of file