Bladeren bron

#933: Support for installing examples via drag and drop of a URL.

* #933: Drag drop support for installing examples.

* Fixed compilation error for luna target.

* Refactored drop listeners.

* Filter out empty categories.

* Automatically download examples from repo on drop installation.

* Added copyright headers.
Thomas Kutz 9 jaren geleden
bovenliggende
commit
12effbdae3

+ 6 - 0
plugins/org.yakindu.sct.examples.wizard/plugin.xml

@@ -37,5 +37,11 @@
       <initializer
               class="org.yakindu.sct.examples.wizard.preferences.ExamplesPreferenceInitializer">
       </initializer>
+   </extension>
+   <extension
+         point="org.eclipse.ui.startup">
+      <startup
+            class="org.yakindu.sct.examples.wizard.drop.ExampleDropSupportRegistrar">
+      </startup>
    </extension>
 </plugin>

+ 5 - 1
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/ExampleWizard.java

@@ -45,7 +45,11 @@ public class ExampleWizard extends Wizard implements INewWizard, ExampleWizardCo
 		setWindowTitle(WINDOW_TITLE);
 		setNeedsProgressMonitor(true);
 		Guice.createInjector(new ExampleWizardModule()).injectMembers(this);
-
+	}
+	
+	public ExampleWizard(String exampleId) {
+		this();
+		page.setInstallExampleId(exampleId);
 	}
 
 	public void init(IWorkbench workbench, IStructuredSelection selection) {

+ 187 - 0
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/drop/DropWorkbenchChangeListener.java

@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2016 committers of YAKINDU 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.examples.wizard.drop;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IPageListener;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+
+/**
+ * Copied from
+ * {@link org.eclipse.epp.internal.mpc.ui.wizards.MarketplaceDropAdapter}.
+ * 
+ */
+public class DropWorkbenchChangeListener
+		implements
+			IPartListener2,
+			IPageListener,
+			IPerspectiveListener,
+			IWindowListener {
+
+	private IDropTargetInstaller dropTargetInstaller = getDropTargetInstaller();
+	
+	protected IDropTargetInstaller getDropTargetInstaller() {
+		return new ExampleDropTargetInstaller();
+	}
+	
+	@Override
+	public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
+		pageChanged(page);
+	}
+
+	@Override
+	public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) {
+	}
+
+	@Override
+	public void pageActivated(IWorkbenchPage page) {
+		pageChanged(page);
+	}
+
+	@Override
+	public void pageClosed(IWorkbenchPage page) {
+	}
+
+	@Override
+	public void pageOpened(IWorkbenchPage page) {
+		pageChanged(page);
+	}
+
+	protected void pageChanged(IWorkbenchPage page) {
+		if (page == null) {
+			return;
+		}
+		IWorkbenchWindow workbenchWindow = page.getWorkbenchWindow();
+		windowChanged(workbenchWindow);
+	}
+
+	@Override
+	public void windowActivated(IWorkbenchWindow window) {
+		windowChanged(window);
+	}
+
+	protected void windowChanged(IWorkbenchWindow window) {
+		if (window == null) {
+			return;
+		}
+		Shell shell = window.getShell();
+		runUpdate(shell);
+	}
+
+	@Override
+	public void windowDeactivated(IWorkbenchWindow window) {
+	}
+
+	@Override
+	public void windowClosed(IWorkbenchWindow window) {
+	}
+
+	@Override
+	public void windowOpened(IWorkbenchWindow window) {
+		hookWindow(window);
+	}
+
+	protected void hookWindow(IWorkbenchWindow window) {
+		if (window == null) {
+			return;
+		}
+		window.addPageListener(this);
+		window.addPerspectiveListener(this);
+		IPartService partService = (IPartService) window.getService(IPartService.class);
+		partService.addPartListener(this);
+		windowChanged(window);
+	}
+
+	@Override
+	public void partOpened(IWorkbenchPartReference partRef) {
+		partUpdate(partRef);
+	}
+
+	@Override
+	public void partActivated(IWorkbenchPartReference partRef) {
+		partUpdate(partRef);
+	}
+
+	@Override
+	public void partBroughtToTop(IWorkbenchPartReference partRef) {
+		partUpdate(partRef);
+	}
+
+	@Override
+	public void partVisible(IWorkbenchPartReference partRef) {
+	}
+
+	@Override
+	public void partClosed(IWorkbenchPartReference partRef) {
+		partUpdate(partRef);
+	}
+
+	@Override
+	public void partDeactivated(IWorkbenchPartReference partRef) {
+		partUpdate(partRef);
+	}
+
+	@Override
+	public void partHidden(IWorkbenchPartReference partRef) {
+		partUpdate(partRef);
+	}
+
+	@Override
+	public void partInputChanged(IWorkbenchPartReference partRef) {
+	}
+
+	protected void partUpdate(IWorkbenchPartReference partRef) {
+		if (partRef == null) {
+			return;
+		}
+		IWorkbenchPage page = partRef.getPage();
+		pageChanged(page);
+	}
+
+	protected void runUpdate(final Shell shell) {
+		if (shell == null || shell.isDisposed()) {
+			return;
+		}
+		Display display = shell.getDisplay();
+		if (display == null || display.isDisposed()) {
+			return;
+		}
+		try {
+			display.asyncExec(new Runnable() {
+
+				public void run() {
+					if (!shell.isDisposed()) {
+						dropTargetInstaller.installDropTarget(shell);
+					}
+				}
+			});
+		} catch (SWTException ex) {
+			if (ex.code == SWT.ERROR_DEVICE_DISPOSED) {
+				// ignore
+				return;
+			}
+			ex.printStackTrace();
+		} catch (RuntimeException ex) {
+			ex.printStackTrace();
+		}
+	}
+
+}

+ 61 - 0
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/drop/ExampleDropSupportRegistrar.java

@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2016 committers of YAKINDU 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.examples.wizard.drop;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.UIJob;
+
+/**
+ * 
+ * Copied from {@link org.eclipse.epp.internal.mpc.ui.wizards.MarketplaceDropAdapter}.
+ *
+ */
+public class ExampleDropSupportRegistrar implements IStartup {
+
+	private final DropWorkbenchChangeListener workbenchListener = new DropWorkbenchChangeListener();
+
+	@Override
+	public void earlyStartup() {
+		registerExampleDropAdapter();
+	}
+	
+	private void registerExampleDropAdapter() {
+		UIJob registerJob = new UIJob(Display.getDefault(), "Registering example drop adapter.") {
+			{
+				setPriority(Job.SHORT);
+				setSystem(true);
+			}
+
+			@Override
+			public IStatus runInUIThread(IProgressMonitor monitor) {
+				IWorkbench workbench = PlatformUI.getWorkbench();
+				workbench.addWindowListener(workbenchListener);
+				IWorkbenchWindow[] workbenchWindows = workbench
+						.getWorkbenchWindows();
+				for (IWorkbenchWindow window : workbenchWindows) {
+					workbenchListener.hookWindow(window);
+				}
+				return Status.OK_STATUS;
+			}
+
+		};
+		registerJob.schedule();
+	}
+
+}

+ 109 - 0
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/drop/ExampleDropTargetInstaller.java

@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2016 committers of YAKINDU 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.examples.wizard.drop;
+
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetAdapter;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.URLTransfer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Copied from
+ * {@link org.eclipse.epp.internal.mpc.ui.wizards.MarketplaceDropAdapter}.
+ * 
+ */
+public class ExampleDropTargetInstaller implements IDropTargetInstaller {
+
+	private final DropTargetAdapter dropListener = getDropTargetListener();
+
+	private Transfer[] transferAgents;
+
+	public void installDropTarget(final Shell shell) {
+		hookUrlTransfer(shell, dropListener);
+	}
+	
+	protected DropTargetAdapter getDropTargetListener() {
+		return new ExampleDropTargetListener();
+	}
+
+	protected DropTarget hookUrlTransfer(final Shell shell, DropTargetAdapter dropListener) {
+		DropTarget target = findDropTarget(shell);
+		if (target != null) {
+			// target exists, get it and check proper registration
+			registerWithExistingTarget(target);
+		} else {
+			target = new DropTarget(shell, DROP_OPERATIONS);
+			if (transferAgents == null) {
+				transferAgents = new Transfer[]{URLTransfer.getInstance()};
+			}
+			target.setTransfer(transferAgents);
+		}
+		registerDropListener(target, dropListener);
+
+		Control[] children = shell.getChildren();
+		for (Control child : children) {
+			hookRecursive(child, dropListener);
+		}
+		return target;
+	}
+
+	protected void registerDropListener(DropTarget target, DropTargetListener dropListener) {
+		target.removeDropListener(dropListener);
+		target.addDropListener(dropListener);
+	}
+
+	protected void hookRecursive(Control child, DropTargetListener dropListener) {
+		DropTarget childTarget = findDropTarget(child);
+		if (childTarget != null) {
+			registerWithExistingTarget(childTarget);
+			registerDropListener(childTarget, dropListener);
+		}
+		if (child instanceof Composite) {
+			Composite composite = (Composite) child;
+			Control[] children = composite.getChildren();
+			for (Control control : children) {
+				hookRecursive(control, dropListener);
+			}
+		}
+	}
+
+	protected void registerWithExistingTarget(DropTarget target) {
+		Transfer[] transfers = target.getTransfer();
+		boolean exists = false;
+		if (transfers != null) {
+			for (Transfer transfer : transfers) {
+				if (transfer instanceof URLTransfer) {
+					exists = true;
+					break;
+				}
+			}
+			if (!exists) {
+				Transfer[] newTransfers = new Transfer[transfers.length + 1];
+				System.arraycopy(transfers, 0, newTransfers, 0, transfers.length);
+				newTransfers[transfers.length] = URLTransfer.getInstance();
+				target.setTransfer(newTransfers);
+			}
+		}
+	}
+
+	protected DropTarget findDropTarget(Control control) {
+		Object object = control.getData(DND.DROP_TARGET_KEY);
+		if (object instanceof DropTarget) {
+			return (DropTarget) object;
+		}
+		return null;
+	}
+}

+ 159 - 0
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/drop/ExampleDropTargetListener.java

@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2016 committers of YAKINDU 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.examples.wizard.drop;
+
+import org.eclipse.jface.util.Util;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetAdapter;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.dnd.URLTransfer;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.yakindu.sct.examples.wizard.ExampleWizard;
+
+/**
+ * Mostly copied from
+ * {@link org.eclipse.epp.internal.mpc.ui.wizards.MarketplaceDropAdapter}.
+ * 
+ * @author Thomas Kutz - Adjusted {@link #proceedInstallation(String)} method to
+ *         open example wizard with preselected example based on dropped URL.
+ * 
+ */
+public class ExampleDropTargetListener extends DropTargetAdapter {
+
+	private static final int[] PREFERRED_DROP_OPERATIONS = {DND.DROP_DEFAULT, DND.DROP_COPY, DND.DROP_MOVE,
+			DND.DROP_LINK};
+
+	@Override
+	public void dragEnter(DropTargetEvent e) {
+		updateDragDetails(e);
+	}
+
+	@Override
+	public void dragOver(DropTargetEvent e) {
+		updateDragDetails(e);
+	}
+
+	@Override
+	public void dragLeave(DropTargetEvent e) {
+		if (e.detail == DND.DROP_NONE) {
+			setDropOperation(e);
+		}
+	}
+
+	@Override
+	public void dropAccept(DropTargetEvent e) {
+		updateDragDetails(e);
+	}
+
+	@Override
+	public void dragOperationChanged(DropTargetEvent e) {
+		updateDragDetails(e);
+	}
+
+	private void setDropOperation(DropTargetEvent e) {
+		int allowedOperations = e.operations;
+		for (int op : PREFERRED_DROP_OPERATIONS) {
+			if ((allowedOperations & op) != 0) {
+				e.detail = op;
+				return;
+			}
+		}
+		e.detail = allowedOperations;
+	}
+
+	private void updateDragDetails(DropTargetEvent e) {
+		if (dropTargetIsValid(e, false)) {
+			setDropOperation(e);
+		}
+	}
+
+	private boolean dropTargetIsValid(DropTargetEvent e, boolean isDrop) {
+		if (URLTransfer.getInstance().isSupportedType(e.currentDataType)) {
+			// on Windows, we get the URL already during drag operations...
+			// FIXME find a way to check the URL early on other platforms,
+			// too...
+			if (isDrop || Util.isWindows()) {
+				if (e.data == null && !extractEventData(e)) {
+					// ... but if we don't, it's no problem, unless this is
+					// already
+					// the final drop event
+					return !isDrop;
+				}
+				final String url = getUrl(e.data);
+				if (!ExampleURLHandler.isValidExample(url)) {
+					return false;
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+
+	private boolean extractEventData(DropTargetEvent e) {
+		TransferData transferData = e.currentDataType;
+		if (transferData != null) {
+			Object data = URLTransfer.getInstance().nativeToJava(transferData);
+			if (data != null && getUrl(data) != null) {
+				e.data = data;
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public void drop(DropTargetEvent event) {
+		if (!URLTransfer.getInstance().isSupportedType(event.currentDataType)) {
+			return;
+		}
+		final String url = getUrl(event.data);
+
+		if (ExampleURLHandler.isValidExample(url)) {
+			DropTarget source = (DropTarget) event.getSource();
+			Display display = source.getDisplay();
+			display.asyncExec(new Runnable() {
+				public void run() {
+					proceedInstallation(url);
+				}
+			});
+		}
+	}
+
+	protected void proceedInstallation(String url) {
+		createWizardDialog(ExampleURLHandler.extractExampleId(url));
+	}
+
+	private void createWizardDialog(String exampleId) {
+		Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+		ExampleWizard exampleWizard = new ExampleWizard(exampleId);
+		WizardDialog dialog = new WizardDialog(shell, exampleWizard);
+		dialog.open();
+	}
+
+	private String getUrl(Object eventData) {
+		if (eventData == null) {
+			return null;
+		}
+		if (eventData == null || !(eventData instanceof String)) {
+			return null;
+		}
+		// Depending on the form the link and browser/os,
+		// we get the url twice in the data separated by new lines
+		String[] dataLines = ((String) eventData).split(System.getProperty("line.separator")); //$NON-NLS-1$
+		String url = dataLines[0];
+		return url;
+	}
+}

+ 66 - 0
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/drop/ExampleURLHandler.java

@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2016 committers of YAKINDU 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.examples.wizard.drop;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+/**
+ * Copied from {@link org.eclipse.epp.mpc.ui.MarketplaceUrlHandler}.
+ * 
+ * @author Thomas Kutz - Adjusted to work with example download URLs.
+ */
+public class ExampleURLHandler {
+
+	private static final String EXAMPLE_INSTALL_URL = "https://github.com/Yakindu/examples/wiki/Downloading-an-example";
+	private static final String SPLIT_REGEX = "&";
+	private static final String EQUALS_REGEX = "=";
+	private static final Object ID_KEY = "id";
+	
+	public static boolean isValidExample(String url) {
+		return url != null && url.contains(EXAMPLE_INSTALL_URL);
+	}
+	
+	public static String extractExampleId(String url) {
+		Map<String, String> parsedQuery = parseQuery(url);
+		return parsedQuery.get(ID_KEY);
+	}
+	
+	/**
+	 * Copied from {@link org.eclipse.epp.mpc.ui.MarketplaceUrlHandler.parseQuery(String)}
+	 */
+	private static Map<String, String> parseQuery(String url) {
+		String query;
+		try {
+			query = new URL(url).getQuery();
+		} catch (MalformedURLException e) {
+			return Maps.newHashMap();
+		}
+		if (query == null) {
+			return Maps.newHashMap();
+		}
+		Map<String, String> values = new LinkedHashMap<String, String>();
+		String[] params = query.split(SPLIT_REGEX);
+		for (String param : params) {
+			String[] keyValue = param.split(EQUALS_REGEX);
+			if (keyValue.length == 2) {
+				String key = keyValue[0];
+				String value = keyValue[1];
+				values.put(key, value);
+			}
+		}
+		return values;
+	}
+}

+ 26 - 0
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/drop/IDropTargetInstaller.java

@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2016 committers of YAKINDU 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	committers of YAKINDU - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.examples.wizard.drop;
+
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * 
+ * @author Thomas Kutz - Initial Contribution and API
+ *
+ */
+public interface IDropTargetInstaller {
+
+	public static final int DROP_OPERATIONS = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_DEFAULT;
+	
+	void installDropTarget(final Shell shell);
+}

+ 1 - 1
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/pages/MessageArea.java

@@ -40,7 +40,7 @@ public class MessageArea extends Composite {
 	protected static final String DISPLAY_ID = "com.yakindu.sct.examples";
 	private Label imageLabel;
 	private Link textLabel;
-	private Button button;
+	public Button button;
 	private Group group;
 
 	public static enum State {

+ 70 - 5
plugins/org.yakindu.sct.examples.wizard/src/org/yakindu/sct/examples/wizard/pages/SelectExamplePage.java

@@ -26,6 +26,8 @@ import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
 import org.eclipse.jface.wizard.WizardPage;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.browser.Browser;
@@ -47,6 +49,8 @@ import org.yakindu.sct.examples.wizard.service.ExampleData;
 import org.yakindu.sct.examples.wizard.service.ExampleWizardConstants;
 import org.yakindu.sct.examples.wizard.service.IExampleService;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 /**
@@ -57,7 +61,11 @@ import com.google.inject.Inject;
  */
 
 public class SelectExamplePage extends WizardPage
-		implements ExampleWizardConstants, ISelectionChangedListener, SelectionListener, IPropertyChangeListener {
+		implements
+			ExampleWizardConstants,
+			ISelectionChangedListener,
+			SelectionListener,
+			IPropertyChangeListener {
 
 	@Inject
 	private IExampleService exampleService;
@@ -66,6 +74,9 @@ public class SelectExamplePage extends WizardPage
 	private Browser browser;
 	private MessageArea messageArea;
 
+	/** ID of example to be installed */
+	private String exampleIdToInstall;
+
 	public SelectExamplePage() {
 		super(SELECT_PAGE_TITLE);
 		setTitle(SELECT_PAGE_TITLE);
@@ -91,7 +102,7 @@ public class SelectExamplePage extends WizardPage
 		container.setLayout(layout);
 		createTreeViewer(container);
 		createDetailsPane(container);
-		container.setWeights(new int[] { 1, 2 });
+		container.setWeights(new int[]{1, 2});
 		setControl(container);
 	}
 
@@ -135,6 +146,10 @@ public class SelectExamplePage extends WizardPage
 
 	}
 
+	private boolean revealExamplesAutomatically() {
+		return (exampleIdToInstall != null) && (!exampleService.exists() || !exampleService.isUpToDate(null));
+	}
+
 	private void initAsync() {
 		try {
 			getWizard().getContainer().run(true, false, new IRunnableWithProgress() {
@@ -143,6 +158,16 @@ public class SelectExamplePage extends WizardPage
 					init(monitor);
 				}
 			});
+			
+			if (revealExamplesAutomatically()) {
+				Display.getCurrent().asyncExec(new Runnable() {
+					@Override
+					public void run() {
+						messageArea.button.setEnabled(false);
+						revealExamples();
+					}
+				});
+			}
 		} catch (InvocationTargetException | InterruptedException e) {
 			e.printStackTrace();
 		}
@@ -178,12 +203,46 @@ public class SelectExamplePage extends WizardPage
 
 	protected void setInput(final IProgressMonitor monitor) {
 		final List<ExampleData> input = exampleService.getExamples(new NullProgressMonitor());
-		
+
 		messageArea.hide();
 		viewer.setInput(input);
 		viewer.expandAll();
 		// explicit layouting required for Unix systems
 		viewer.getControl().getParent().getParent().layout(true);
+
+		filterAndSelectExampleToInstall(viewer, input);
+	}
+
+	protected void filterAndSelectExampleToInstall(TreeViewer viewer, List<ExampleData> input) {
+		final ExampleData exampleToInstall = Iterables.find(input, new Predicate<ExampleData>() {
+			@Override
+			public boolean apply(ExampleData input) {
+				if (exampleIdToInstall != null) {
+					return exampleIdToInstall.equals(input.getId());
+				}
+				return true;
+			}
+
+		});
+		if (exampleToInstall != null) {
+			viewer.addFilter(new ViewerFilter() {
+
+				@Override
+				public boolean select(Viewer viewer, Object parentElement, Object element) {
+					if (exampleIdToInstall == null) {
+						return true;
+					}
+					if (element instanceof ExampleData) {
+						return exampleIdToInstall.equals(((ExampleData) element).getId());
+					}
+					if (element instanceof ExampleContentProvider.Category) {
+						return ((ExampleContentProvider.Category)element).getChildren().contains(exampleToInstall);
+					}
+					return true;
+				}
+			});
+			viewer.setSelection(new StructuredSelection(exampleToInstall), true);
+		}
 	}
 
 	protected void createTreeViewer(Composite container) {
@@ -227,6 +286,10 @@ public class SelectExamplePage extends WizardPage
 
 	@Override
 	public void widgetSelected(SelectionEvent e) {
+		revealExamples();
+	}
+
+	protected void revealExamples() {
 		try {
 			getWizard().getContainer().run(true, true, new IRunnableWithProgress() {
 				@Override
@@ -248,13 +311,11 @@ public class SelectExamplePage extends WizardPage
 		} catch (InvocationTargetException | InterruptedException e1) {
 			e1.printStackTrace();
 		}
-
 	}
 
 	@Override
 	public void widgetDefaultSelected(SelectionEvent e) {
 		widgetSelected(e);
-
 	}
 
 	@Override
@@ -264,4 +325,8 @@ public class SelectExamplePage extends WizardPage
 		}
 	}
 
+	public void setInstallExampleId(String exampleId) {
+		this.exampleIdToInstall = exampleId;
+	}
+
 }