connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/internal/ui/ConnectionStatusSelectorContribution.java
branchRCL_2_4
changeset 858 1ef0dbc67108
child 1588 be9ca32c0fe2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connectivity/com.nokia.carbide.remoteConnections/src/com/nokia/carbide/remoteconnections/internal/ui/ConnectionStatusSelectorContribution.java	Mon Feb 01 13:30:39 2010 -0600
@@ -0,0 +1,551 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+
+package com.nokia.carbide.remoteconnections.internal.ui;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.ToolTip;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.menus.CommandContributionItem;
+import org.eclipse.ui.menus.WorkbenchWindowControlContribution;
+
+import com.nokia.carbide.remoteconnections.Messages;
+import com.nokia.carbide.remoteconnections.RemoteConnectionsActivator;
+import com.nokia.carbide.remoteconnections.interfaces.IConnection;
+import com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager;
+import com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionListener;
+import com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionsManagerListener;
+import com.nokia.carbide.remoteconnections.internal.api.IConnection2;
+import com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatus;
+import com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatusChangedListener;
+import com.nokia.carbide.remoteconnections.internal.registry.Registry;
+import com.nokia.carbide.remoteconnections.view.ConnectionsView;
+import com.nokia.cpp.internal.api.utils.core.TextUtils;
+import com.nokia.cpp.internal.api.utils.ui.WorkbenchUtils;
+
+
+/**
+ * This widget appears in the Eclipse trim and allows the user to select the
+ * "current" device connection and also see its status at a glance.
+ * <p>
+ * Note: the UI for this control should behave similarly to that of the News Reader
+ * trim.  Due to the way we're modifying the icon and the state dynamically
+ * for this contribution, they can't be implemented the same way, however.
+ */
+@SuppressWarnings("deprecation")
+public class ConnectionStatusSelectorContribution extends WorkbenchWindowControlContribution {
+
+	/**
+	 * This is the id on the command in the toolbar contribution associated with this 
+	 * widget.  Keep this in sync with the extension point!
+	 */
+	private static final String OPEN_REMOTE_CONNECTIONS_VIEW_COMMAND_ID = "openRemoteConnectionsView"; //$NON-NLS-1$
+	private Composite container;
+	private CLabel connectionInfo;
+	private ToolItem connectionIcon;
+	private IConnectionsManager manager;
+	private IConnection currentConnection;
+	private ListenerBlock listenerBlock;
+	private ToolTip tooltip;
+	private MouseAdapter toolbarListener;
+	private ToolBar toolbar;
+
+	/**
+	 * Contains all the listeners.  In most cases we just recreate the contribution status item.
+	 */
+	class ListenerBlock implements IConnectionListener, IConnectionsManagerListener, IConnectionStatusChangedListener {
+
+		/* (non-Javadoc)
+		 * @see com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionListener#connectionAdded(com.nokia.carbide.remoteconnections.interfaces.IConnection)
+		 */
+		public void connectionAdded(IConnection connection) {
+			updateUI();
+			boolean display = (connection instanceof IConnection2 && ((IConnection2) connection).isDynamic());
+			if (display)
+				launchBubble(MessageFormat.format(
+						Messages.getString("ConnectionStatusSelectorContribution.AddedConnectionFormat"),  //$NON-NLS-1$
+						connection.getDisplayName()));
+		}
+		
+		/* (non-Javadoc)
+		 * @see com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionListener#connectionRemoved(com.nokia.carbide.remoteconnections.interfaces.IConnection)
+		 */
+		public void connectionRemoved(IConnection connection) {
+			updateUI();
+			boolean display = (connection instanceof IConnection2 && ((IConnection2) connection).isDynamic());
+			if (display) 
+				launchBubble(MessageFormat.format(
+						Messages.getString("ConnectionStatusSelectorContribution.RemovedConnectionFormat"),  //$NON-NLS-1$
+						connection.getDisplayName()));
+		}
+		
+		/* (non-Javadoc)
+		 * @see com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionListener#currentConnectionSet(com.nokia.carbide.remoteconnections.interfaces.IConnection)
+		 */
+		public void currentConnectionSet(IConnection connection) {
+			updateUI();
+		}
+
+		/* (non-Javadoc)
+		 * @see com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionsManagerListener#connectionStoreChanged()
+		 */
+		public void connectionStoreChanged() {
+			updateUI();
+		}
+
+		/* (non-Javadoc)
+		 * @see com.nokia.carbide.remoteconnections.interfaces.IConnectionsManager.IConnectionsManagerListener#displayChanged()
+		 */
+		public void displayChanged() {
+			updateUI();
+		}
+
+		/* (non-Javadoc)
+		 * @see com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatusChangedListener#statusChanged(com.nokia.carbide.remoteconnections.internal.api.IConnection2.IConnectionStatus)
+		 */
+		public void statusChanged(IConnectionStatus status) {
+			updateConnectionStatus(status);
+		}
+
+	}
+	
+	public ConnectionStatusSelectorContribution() {
+		manager = RemoteConnectionsActivator.getConnectionsManager();
+		listenerBlock = new ListenerBlock();
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * @see org.eclipse.jface.action.ControlContribution#createControl(org.eclipse.swt.widgets.Composite)
+	 */
+	@Override
+	protected Control createControl(Composite parent) {
+		
+		// This UI is recreated whenever the current connection changes.
+		
+		// This is gross.  The normal parent of this is a toolbar,
+		// but we cannot add arbitrary stuff to the toolbar besides
+		// this control.  (If you try, it will usually show up
+		// out of order!)
+		//
+		// But we want to have a toolbar button for the connection icon
+		// for which we can change the tooltip and image, and attach 
+		// a mouse listener responds to left and right clicking.
+		//
+		// In order to do this, we need to poke around in the widget tree
+		// to find the expected Eclipse-generated toolitem.  Unfortunately,
+		// controlling all this ourselves is even uglier (I tried).
+
+		if (parent instanceof ToolBar) {
+			toolbar = (ToolBar) parent;
+			ToolItem[] items = toolbar.getItems();
+			for (ToolItem item : items) {
+				Object data = item.getData();
+				if (data instanceof CommandContributionItem &&
+						((CommandContributionItem) data).getId().contains(OPEN_REMOTE_CONNECTIONS_VIEW_COMMAND_ID)) {
+					connectionIcon = item;
+					break;
+				}
+			}
+		} else {
+			toolbar = null;
+		}
+		
+		container = new Composite(parent, SWT.NONE);
+		GridLayoutFactory.fillDefaults().margins(2, 0).applyTo(container);
+
+		// Create a label for the trim, outside the toolbar.
+		connectionInfo = new CLabel(container, SWT.NONE);
+		GridDataFactory.fillDefaults().grab(false, true).applyTo(connectionInfo);
+
+		String text = Messages.getString("ConnectionStatusSelectorContribution_NoCurrentConnectionMessage"); //$NON-NLS-1$
+		currentConnection = manager.getCurrentConnection();
+		if (currentConnection != null)
+			text = currentConnection.getDisplayName();
+		
+		connectionInfo.setText(text);
+
+		attachListeners();
+		
+		updateConnectionStatus(getConnectionStatus(currentConnection));
+		
+
+		// Yuck, toolbars and items have a wonky UI.  We need to do these events on the parent,
+		// since the ToolItem itself is just an area inside the parent.  (#getControl() is only for separators ?!)
+			
+		// On icon: left click = open view, right click = menu
+		
+		if (toolbar != null) {
+			if (toolbarListener != null)
+				toolbar.removeMouseListener(toolbarListener);
+			
+			toolbarListener = new MouseAdapter() {
+				/* (non-Javadoc)
+				 * @see org.eclipse.swt.events.MouseAdapter#mouseDown(org.eclipse.swt.events.MouseEvent)
+				 */
+				@Override
+				public void mouseDown(MouseEvent event) {
+					ToolItem item = toolbar.getItem(new Point(event.x, event.y));
+					if (item == connectionIcon) {
+						if (event.button == 1) {
+							openConnectionsView();
+						} else if (event.button == 3) {
+							Point screenLoc = toolbar.toDisplay(event.x, event.y);
+							handleConnectionMenu(screenLoc);
+						}
+					}
+				}
+			};
+			toolbar.addMouseListener(toolbarListener);
+			
+			// On label: left or right click = menu
+			connectionInfo.addMouseListener(new MouseAdapter() {
+				public void mouseDown(MouseEvent event) {
+					if (event.button == 1 || event.button == 3) {
+						Point screenLoc = toolbar.toDisplay(event.x, event.y);
+						handleConnectionMenu(screenLoc);
+					}
+				}
+			});
+		}
+		
+		RemoteConnectionsActivator.setHelp(container, "ConnectionStatusSelector"); //$NON-NLS-1$
+		return container;
+	}
+
+	private void handleConnectionMenu(Point screenLoc) {
+		Shell shell = connectionInfo.getParent().getShell();
+		final Display display = shell.getDisplay();
+		
+		final Menu menu = new Menu(shell, SWT.POP_UP);
+		populateConnectionMenu(menu);
+		
+		menu.setLocation(screenLoc.x, screenLoc.y);
+		menu.setVisible(true);
+		
+		while (!menu.isDisposed() && menu.isVisible()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+		menu.dispose();
+	
+	}
+
+	/**
+	 * @return
+	 */
+	protected void populateConnectionMenu(Menu menu) {
+		// Display the connections with dynamic ones first, 
+		// then static ones, separated by a separator
+	
+		List<IConnection> dynamicConnections = new ArrayList<IConnection>();
+		List<IConnection> staticConnections = new ArrayList<IConnection>();
+		for (IConnection connection : RemoteConnectionsActivator.getConnectionsManager().getConnections()) {
+			if (connection instanceof IConnection2 && ((IConnection2)connection).isDynamic()) 
+				dynamicConnections.add(connection);
+			else
+				staticConnections.add(connection);
+		}
+		
+	
+		Comparator<IConnection> connectionComparator = new Comparator<IConnection>() {
+			public int compare(IConnection o1, IConnection o2) {
+				return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName());
+			}
+		};
+		Collections.sort(dynamicConnections, connectionComparator);
+		Collections.sort(staticConnections, connectionComparator);
+	
+		MenuItem label = new MenuItem(menu, SWT.NONE);
+		label.setEnabled(false);
+		
+		int number = 1;
+		if (dynamicConnections.size() + staticConnections.size() == 0) {
+			label.setText(Messages.getString("ConnectionStatusSelectorContribution.NoConnectionsDefinedOrDetected")); //$NON-NLS-1$
+		} else {
+			label.setText(Messages.getString("ConnectionStatusSelectorContribution_SelectTheCurrentConnectionMessage")); //$NON-NLS-1$
+			
+			for (IConnection connection : dynamicConnections) {
+				createConnectionMenuItem(menu, connection, currentConnection, number++);
+			}
+			
+			if (!staticConnections.isEmpty())
+				new MenuItem(menu, SWT.SEPARATOR);
+			
+			for (IConnection connection : staticConnections) {
+				createConnectionMenuItem(menu, connection, currentConnection, number++);
+			}
+		}
+		
+		new MenuItem(menu, SWT.SEPARATOR);
+		
+		MenuItem openView = new MenuItem(menu, SWT.PUSH);
+		openView.setText(Messages.getString("ConnectionStatusSelectorContribution.OpenRemoteConnectionsView")); //$NON-NLS-1$
+		openView.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				openConnectionsView();
+			}
+		});
+	}
+
+	/**
+	 * @param menu
+	 * @param connection
+	 * @param currentConnection 
+	 */
+	private MenuItem createConnectionMenuItem(Menu menu, 
+			final IConnection connection, 
+			IConnection currentConnection,
+			int number) {
+		MenuItem item = new MenuItem(menu, SWT.CHECK);
+		
+		boolean isCurrent = false;
+		isCurrent = connection.equals(currentConnection);
+		
+		item.setSelection(isCurrent);
+		
+		item.setText(MessageFormat.format("&{0} - {1}", number, connection.getDisplayName())); //$NON-NLS-1$
+		
+		item.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				manager.setCurrentConnection(connection);
+			}
+		});		
+		
+		return item;
+	}
+
+	private void attachListeners() {
+		manager.addConnectionListener(listenerBlock);
+		Registry.instance().addConnectionStoreChangedListener(listenerBlock);
+		
+		if (currentConnection != null) {
+			if (currentConnection instanceof IConnection2) {
+				((IConnection2) currentConnection).addStatusChangedListener(listenerBlock);
+			}
+		}
+	}
+
+	private void removeListeners() {
+		if (currentConnection != null) {
+			if (currentConnection instanceof IConnection2) {
+				((IConnection2) currentConnection).removeStatusChangedListener(listenerBlock);
+			}
+		}
+		manager.removeConnectionListener(listenerBlock);
+		Registry.instance().removeConnectionStoreChangedListener(listenerBlock);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see org.eclipse.jface.action.ContributionItem#dispose()
+	 */
+	public void dispose() {
+		removeListeners();
+		if (connectionIcon != null)
+			connectionIcon.dispose();
+		if (toolbarListener != null && !toolbar.isDisposed())
+			toolbar.removeMouseListener(toolbarListener);
+		if (connectionInfo != null)
+			connectionInfo.dispose();
+		
+		super.dispose();
+	}
+
+	/**
+	 * @param ev
+	 */
+	protected void openConnectionsView() {
+		try {
+			IViewPart view = WorkbenchUtils.getView(ConnectionsView.VIEW_ID);
+			if (currentConnection != null && view instanceof ConnectionsView) {
+				((ConnectionsView) view).setSelectedConnection(currentConnection);
+			}
+        } 
+        catch (PartInitException e) {
+        	RemoteConnectionsActivator.logError(e);
+        }
+	}
+
+	/**
+	 * @param currentConnection
+	 * @param status
+	 * @return
+	 */
+	private String createConnectionStatusTooltip(IConnection currentConnection,
+			IConnectionStatus status) {
+		if (currentConnection == null) {
+			return Messages.getString("ConnectionStatusSelectorContribution.NoDynamicOrManualConnectionsTooltip"); //$NON-NLS-1$
+		}
+		
+		String statusString = null;
+		if (status != null) {
+			statusString = createStatusString(status);
+		}
+		
+		if (TextUtils.isEmpty(statusString)) {
+			// fallback string; the status should not be empty
+			if (ConnectionUIUtils.isSomeServiceInUse(currentConnection))
+				statusString = Messages.getString("ConnectionStatusSelectorContribution.InUse"); //$NON-NLS-1$
+			else
+				statusString = Messages.getString("ConnectionStatusSelectorContribution.NotInUse"); //$NON-NLS-1$
+		}
+		
+		return MessageFormat.format(Messages.getString("ConnectionStatusSelectorContribution.ConnectionStatusFormat"), currentConnection.getDisplayName(), statusString); //$NON-NLS-1$
+	}
+
+	private String createStatusString(IConnectionStatus status) {
+		String shortDescription = status.getShortDescription();
+		if (shortDescription == null || shortDescription.length() == 0)
+			return ""; //$NON-NLS-1$
+		String pattern = Messages.getString("ConnectionStatusSelectorContribution.StatusFormat"); //$NON-NLS-1$
+		if (shortDescription != null)
+			shortDescription = shortDescription.toLowerCase();
+		return MessageFormat.format(pattern, shortDescription, status.getLongDescription());
+	}
+
+	/**
+	 * Get the image representing the connection status.
+	 * @param connection
+	 * @return Image, to be disposed
+	 */
+	private IConnectionStatus getConnectionStatus(IConnection connection) {
+		if (!(connection instanceof IConnection2)) {
+			return null;
+		} else {
+			return ((IConnection2) connection).getStatus();
+		}
+	}
+	
+	/**
+	 * @param status
+	 */
+	private void updateConnectionStatus(final IConnectionStatus status) {
+		Display.getDefault().asyncExec(new Runnable() {
+			public void run() {
+				if (connectionIcon == null || connectionIcon.isDisposed())
+					return;
+				
+				Image statusImage;
+				if (status != null)
+					statusImage = ConnectionUIUtils.getConnectionStatusImage(status);
+				else
+					statusImage = ConnectionUIUtils.getConnectionImage(currentConnection);
+				
+				connectionIcon.setImage(statusImage);
+				String tip = createConnectionStatusTooltip(currentConnection, status);
+				connectionInfo.setToolTipText(tip);
+				
+				String preamble = Messages.getString("ConnectionStatusSelectorContribution.IconTooltipPrefixMessage"); //$NON-NLS-1$
+				connectionIcon.setToolTipText(preamble + tip);		
+			}
+		});
+		
+	}
+
+	/**
+	 * 
+	 */
+	private void updateUI() {
+		// perform update in UI thread
+		final IWorkbench workbench = PlatformUI.getWorkbench();
+		workbench.getDisplay().asyncExec(new Runnable() {
+			public void run() {
+				IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+				if (window != null) {
+					update();
+				}
+			}
+		});		
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see org.eclipse.jface.action.ContributionItem#isDynamic()
+	 */
+	@Override
+	public boolean isDynamic() {
+		return true;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see org.eclipse.jface.action.ContributionItem#update()
+	 */
+	@Override
+	public void update() {
+		getParent().update(true);
+	}
+	
+	/**
+	 * Show a status bubble for important changes.
+	 * @param string
+	 */
+	public void launchBubble(final String string) {
+		Display.getDefault().asyncExec(new Runnable() {
+			public void run() {
+				if (tooltip != null)
+					tooltip.dispose();
+				
+				if (connectionInfo == null || connectionInfo.isDisposed())
+					return;
+				
+				tooltip = new ToolTip(connectionInfo.getParent().getShell(), SWT.BALLOON | SWT.ICON_INFORMATION);
+				tooltip.setMessage(string);
+				Rectangle bounds = connectionInfo.getBounds();
+				Point center = new Point(bounds.x + bounds.width / 2, 
+						bounds.y + bounds.height);
+				Point location = connectionInfo.getParent().toDisplay(center);
+				//System.out.println(connectionInfo.hashCode() + ": " + connectionInfo.getLocation() + " : " + location);
+				tooltip.setLocation(location);
+				tooltip.setVisible(true);
+			}
+		});
+	}
+
+
+}
\ No newline at end of file