[466264] Enhance UX in simple installer

First version of UI redesign of simple installer mode.

https://bugs.eclipse.org/bugs/show_bug.cgi?id=466264

Change-Id: I97e31cd19661daa557ffd7ae5bf74d29afd24751
Signed-off-by: Andreas Scharf <scharf@yatta.de>
Signed-off-by: Eike Stepper <stepper@esc-net.de>
diff --git a/plugins/org.eclipse.oomph.jreinfo.ui/src/org/eclipse/oomph/jreinfo/ui/JREController.java b/plugins/org.eclipse.oomph.jreinfo.ui/src/org/eclipse/oomph/jreinfo/ui/JREController.java
index e533d50..38e5abb 100644
--- a/plugins/org.eclipse.oomph.jreinfo.ui/src/org/eclipse/oomph/jreinfo/ui/JREController.java
+++ b/plugins/org.eclipse.oomph.jreinfo.ui/src/org/eclipse/oomph/jreinfo/ui/JREController.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.jreinfo.ui;
 
@@ -26,6 +27,7 @@
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.StructuredViewer;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
@@ -60,6 +62,8 @@
 
   private boolean refreshing;
 
+  private Color viewerForegroundColor;
+
   public JREController(Label label, StructuredViewer viewer, Request.Handler downloadHandler)
   {
     this.label = label;
@@ -69,6 +73,7 @@
     if (viewer != null)
     {
       viewer.addSelectionChangedListener(this);
+      viewerForegroundColor = viewer.getControl().getForeground();
     }
   }
 
@@ -269,6 +274,7 @@
   protected void setLabel(String text)
   {
     label.setText(text);
+    label.getParent().layout();
   }
 
   protected JREFilter createJREFilter()
@@ -290,7 +296,7 @@
       }
       else
       {
-        control.setForeground(null);
+        control.setForeground(viewerForegroundColor);
       }
     }
   }
diff --git a/plugins/org.eclipse.oomph.p2.ui/src/org/eclipse/oomph/p2/internal/ui/AgentManagerDialog.java b/plugins/org.eclipse.oomph.p2.ui/src/org/eclipse/oomph/p2/internal/ui/AgentManagerDialog.java
index f3ff9d5..6847259 100644
--- a/plugins/org.eclipse.oomph.p2.ui/src/org/eclipse/oomph/p2/internal/ui/AgentManagerDialog.java
+++ b/plugins/org.eclipse.oomph.p2.ui/src/org/eclipse/oomph/p2/internal/ui/AgentManagerDialog.java
@@ -24,7 +24,7 @@
 {
   public static final String TITLE = "Bundle Pool Management";
 
-  private static final String MESSAGE = "Manage your p2 agents and bundle pools.";
+  public static final String MESSAGE = "Manage your p2 agents and bundle pools";
 
   private Object selectedElement;
 
@@ -65,7 +65,7 @@
   @Override
   protected String getDefaultMessage()
   {
-    return MESSAGE;
+    return MESSAGE + ".";
   }
 
   @Override
@@ -98,7 +98,7 @@
       protected void profilesShown(boolean profilesShown)
       {
         super.profilesShown(profilesShown);
-        String message = MESSAGE;
+        String message = MESSAGE + ".";
         if (profilesShown)
         {
           message += " Double-click profiles to see their details.";
diff --git a/plugins/org.eclipse.oomph.setup.installer/build.properties b/plugins/org.eclipse.oomph.setup.installer/build.properties
index 02c6311..64b3c79 100644
--- a/plugins/org.eclipse.oomph.setup.installer/build.properties
+++ b/plugins/org.eclipse.oomph.setup.installer/build.properties
@@ -19,7 +19,9 @@
                about.properties,\
                about.mappings,\
                about.ini,\
-               splash.bmp
+               splash.bmp,\
+               html/,\
+               fonts/
 src.includes = about.html,\
                ProductCatalogGenerator.launch,\
                pom.xml
diff --git a/plugins/org.eclipse.oomph.setup.installer/fonts/OpenSans-Regular.ttf b/plugins/org.eclipse.oomph.setup.installer/fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..db43334
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/fonts/OpenSans-Regular.ttf
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/html/PageTemplate.html b/plugins/org.eclipse.oomph.setup.installer/html/PageTemplate.html
new file mode 100644
index 0000000..af943ff
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/html/PageTemplate.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style type="text/css">%INSTALLER_CSS%</style>
+<title>eclipseInstaller</title>
+</head>
+<body>
+	%CONTENT%
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/org.eclipse.oomph.setup.installer/html/ProductTemplate.html b/plugins/org.eclipse.oomph.setup.installer/html/ProductTemplate.html
new file mode 100644
index 0000000..b093ae6
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/html/ProductTemplate.html
@@ -0,0 +1,9 @@
+<div class="%PRODUCT_CONTAINER_STYLE%" onclick="location.href='%PRODUCT_LINK%';">
+	<div class="icon">
+		<img src="%PRODUCT_ICON_SRC%">
+	</div>
+	<div class="product">
+		<div class="productTitle">%PRODUCT_TITLE%</div>
+		<div class="productDescription">%PRODUCT_DESCRIPTION%</div>
+	</div>
+</div>
diff --git a/plugins/org.eclipse.oomph.setup.installer/html/css/simpleInstaller.css b/plugins/org.eclipse.oomph.setup.installer/html/css/simpleInstaller.css
new file mode 100644
index 0000000..5f3f014
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/html/css/simpleInstaller.css
@@ -0,0 +1,110 @@
+html, body, div, span, img {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	outline: 0;
+	font-size: 100%;
+	vertical-align: baseline;
+	background: transparent;
+}
+
+body {
+	line-height: 1;
+}
+
+a {
+	margin: 0;
+	padding: 0;
+	font-size: 100%;
+	vertical-align: baseline;
+	background: transparent;
+}
+
+.productContainer {
+	font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+	width: 470px;
+	height: 62px;
+	padding: 18px 14px;
+	border-bottom: 3px solid #eee;
+}
+
+.productContainer:last-child {
+	border-bottom: 0;
+}
+
+.largeProduct {
+	height: 180px !important;
+}
+
+.largeProduct:hover {
+	background-color: transparent !important;
+}
+
+.largeProduct div.product {
+	height: auto !important;
+}
+
+.largeProduct div.productDescription {
+	height: 140px;
+	overflow-x: hidden;
+	overflow-y: auto;
+}
+
+.productLink {
+	cursor: pointer;
+}
+
+.noSelect {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.productContainer:hover {
+	background-color: #aebbdd;
+}
+
+.icon {
+	float: left;
+	width: 64px;
+	height: 64px;
+	position: relative;
+	background-color: #eeeeee;
+	border-radius: 32px;
+}
+
+.icon img {
+	display: block;
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	transform: translate(-50%, -50%);
+	-webkit-transform: translate(-50%, -50%);
+	-moz-transform: translate(-50%, -50%);
+	-ms-transform: translate(-50%, -50%);
+	-o-transform: translate(-50%, -50%);
+}
+
+.product {
+	float: left;
+	width: 390px;
+	height: 56px;
+	padding: 6px 0 2px 16px;
+	overflow: visible;
+}
+
+.productTitle {
+	font-size: 16px;
+	color: #3d3364;
+	font-weight: bold;
+	margin-bottom: 10px;
+}
+
+.productDescription {
+	font-size: 13px;
+	color: #555555;
+	line-height: 15px;
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit.png
new file mode 100644
index 0000000..9881781
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit_hover.png
new file mode 100644
index 0000000..a78eb4e
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit.png
new file mode 100644
index 0000000..0621339
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit_hover.png
new file mode 100644
index 0000000..499a9af
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left.png
new file mode 100644
index 0000000..8da050e
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_disabled.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_disabled.png
new file mode 100644
index 0000000..3dd72bc
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_disabled.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_hover.png
new file mode 100644
index 0000000..941bdd1
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/checkmark_checked.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/checkmark_checked.png
new file mode 100644
index 0000000..4fe6398
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/checkmark_checked.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/close.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close.png
new file mode 100644
index 0000000..db9189f
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_hover.png
new file mode 100644
index 0000000..0107aef
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message.png
new file mode 100644
index 0000000..3504a20
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message_hover.png
new file mode 100644
index 0000000..341e073
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/delete.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/delete.png
new file mode 100644
index 0000000..2074e49
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/delete.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle.png
new file mode 100644
index 0000000..d752e58
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle_hover.png
new file mode 100644
index 0000000..5e4e5ce
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png
index fddbc2b..5e1a4f1 100644
--- a/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit_hover.png
new file mode 100644
index 0000000..5a867b3
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png
index 472484f..bf6ed9e 100644
--- a/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_disabled.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_disabled.png
new file mode 100644
index 0000000..e239ba2
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_disabled.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_hover.png
new file mode 100644
index 0000000..2324d88
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install.png
new file mode 100644
index 0000000..7fc3036
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install_hover.png
new file mode 100644
index 0000000..2b571bd
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch.png
new file mode 100644
index 0000000..74d41f4
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch_hover.png
new file mode 100644
index 0000000..abb74d4
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu.png
new file mode 100644
index 0000000..0cc202d
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_disabled.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_disabled.png
new file mode 100644
index 0000000..59c3bb3
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_disabled.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_hover.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_hover.png
new file mode 100644
index 0000000..a3dd77f
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_hover.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/notification_overlay.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/notification_overlay.png
new file mode 100644
index 0000000..2f305a5
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/notification_overlay.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/search.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/search.png
new file mode 100644
index 0000000..50addfa
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/search.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png
index a52e95e..ad208e1 100644
--- a/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png
+++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png
Binary files differ
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/AbstractSimpleDialog.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/AbstractSimpleDialog.java
index 0e1b6ce..c48fc82 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/AbstractSimpleDialog.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/AbstractSimpleDialog.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
@@ -14,13 +15,12 @@
 import org.eclipse.oomph.ui.ShellMove;
 import org.eclipse.oomph.ui.UIUtil;
 
+import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.window.Window;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.TraverseEvent;
 import org.eclipse.swt.events.TraverseListener;
-import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
@@ -33,18 +33,9 @@
  */
 public abstract class AbstractSimpleDialog extends Shell
 {
-  public static final Color WHITE = UIUtil.getDisplay().getSystemColor(SWT.COLOR_WHITE);
-
   private static final ShellMove SHELL_MOVE = new ShellMove()
   {
     @Override
-    public void hookControl(Control control)
-    {
-      control.setBackground(WHITE);
-      super.hookControl(control);
-    }
-
-    @Override
     protected boolean shouldHookControl(Control control)
     {
       return super.shouldHookControl(control) || control instanceof SimpleInstallerPage;
@@ -55,18 +46,22 @@
 
   private int returnCode = Window.OK;
 
-  public AbstractSimpleDialog(Display display, int style, int width, int height, int marginWidth, int marginHeight)
+  public AbstractSimpleDialog(Display display, int style, int width, int height)
   {
     super(display, style);
 
     GridLayout verticalLayout = UIUtil.createGridLayout(1);
-    verticalLayout.verticalSpacing = 20;
+    verticalLayout.marginWidth = 1;
+    verticalLayout.marginHeight = 1;
+    verticalLayout.verticalSpacing = 0;
 
     setLayout(verticalLayout);
     setSize(width, height);
     setImages(Window.getDefaultImages());
     setText(AbstractSetupDialog.SHELL_TEXT);
 
+    setBackground(SetupInstallerPlugin.getColor(207, 207, 207));
+
     Rectangle bounds = display.getPrimaryMonitor().getBounds();
     setLocation(bounds.x + (bounds.width - width) / 2, bounds.y + (bounds.height - height) / 2);
 
@@ -83,17 +78,20 @@
       }
     });
 
-    GridLayout titleLayout = UIUtil.createGridLayout(4);
-    titleLayout.marginTop = marginHeight;
-    titleLayout.marginWidth = marginWidth;
+    GridLayout titleLayout = UIUtil.createGridLayout(2);
     titleLayout.horizontalSpacing = 0;
+    titleLayout.verticalSpacing = 0;
+    titleLayout.marginLeft = 20;
+    titleLayout.marginRight = 14;
 
     titleComposite = new Composite(this, SWT.NONE);
-    titleComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+    titleComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 72).create());
     titleComposite.setLayout(titleLayout);
+    titleComposite.setBackgroundMode(SWT.INHERIT_FORCE);
+    titleComposite.setBackground(SetupInstallerPlugin.COLOR_LIGHTEST_GRAY);
 
     Label titleImage = new Label(titleComposite, SWT.NONE);
-    titleImage.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, true, false));
+    titleImage.setLayoutData(GridDataFactory.swtDefaults().grab(true, true).indent(SWT.DEFAULT, 26).align(SWT.BEGINNING, SWT.BEGINNING).create());
     titleImage.setImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/title.png"));
   }
 
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallLaunchButton.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallLaunchButton.java
new file mode 100644
index 0000000..a813ac0
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallLaunchButton.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
+import org.eclipse.oomph.ui.UIUtil;
+
+import org.eclipse.emf.common.util.URI;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @author Andreas Scharf
+ */
+public class InstallLaunchButton extends ImageHoverButton
+{
+  private static final Color COLOR_FOREGROUND_DEFAULT = SetupInstallerPlugin.COLOR_WHITE;
+
+  private static final Color COLOR_INSTALL = SetupInstallerPlugin.getColor(250, 148, 0);
+
+  private static final Color COLOR_INSTALLING = SetupInstallerPlugin.getColor(50, 196, 0);
+
+  private static final Color COLOR_INSTALLING_FOREGROUND = SetupInstallerPlugin.COLOR_LABEL_FOREGROUND;
+
+  private static final Color COLOR_LAUNCH = COLOR_INSTALLING;
+
+  private State currentState;
+
+  private float progress;
+
+  public InstallLaunchButton(Composite parent)
+  {
+    super(parent, SWT.PUSH);
+
+    setForeground(UIUtil.getDisplay().getSystemColor(SWT.COLOR_WHITE));
+    setFont(SetupInstallerPlugin.getFont(getFont(), URI.createURI("font:///14/bold")));
+    setCornerWidth(10);
+    setAlignment(SWT.CENTER);
+    setCurrentState(State.INSTALL);
+  }
+
+  public float getProgress()
+  {
+    return progress;
+  }
+
+  public void setProgress(float progress)
+  {
+    if (progress < 0 || progress > 1)
+    {
+      throw new IllegalArgumentException("Progress must be in [0..1]");
+    }
+
+    this.progress = progress;
+    redraw();
+  }
+
+  public void setCurrentState(State newState)
+  {
+    if (newState == null)
+    {
+      throw new IllegalArgumentException("New state cannot be null!");
+    }
+
+    if (currentState != newState)
+    {
+      State oldState = currentState;
+      currentState = newState;
+      stateChanged(oldState, currentState);
+    }
+  }
+
+  private void stateChanged(State oldState, State newState)
+  {
+    setDefaultImage(newState.icon);
+    setHoverImage(newState.hoverIcon);
+    setText(newState.label);
+    setBackground(newState.backgroundColor);
+    setForeground(newState.foregroundColor);
+
+    switch (newState)
+    {
+      case INSTALLING:
+        setListenersPaused(true);
+        setCursor(null);
+        setShowButtonDownState(false);
+        break;
+
+      default:
+        setListenersPaused(false);
+        setCursor(UIUtil.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+        setShowButtonDownState(true);
+    }
+  }
+
+  public State getCurrentState()
+  {
+    return currentState;
+  }
+
+  @Override
+  public void drawBackground(GC gc, int x, int y, int width, int height, int offsetX, int offsetY)
+  {
+    if (currentState == State.INSTALLING)
+    {
+      gc.setBackground(SetupInstallerPlugin.COLOR_LIGHTEST_GRAY);
+      gc.fillRoundRectangle(x, y, width, height, getCornerWidth(), getCornerWidth());
+
+      int progressWidth = (int)(width * progress);
+      gc.setBackground(currentState.backgroundColor);
+      gc.fillRoundRectangle(x, y, progressWidth, height, getCornerWidth(), getCornerWidth());
+
+      // Check if we should draw a hard edge.
+      if (progressWidth <= width - getCornerWidth() / 2)
+      {
+        gc.fillRectangle(progressWidth - getCornerWidth(), y, getCornerWidth(), height);
+      }
+    }
+    else
+    {
+      super.drawBackground(gc, x, y, width, height, offsetX, offsetY);
+    }
+  }
+
+  /**
+   * @author Andreas Scharf
+   */
+  public static enum State
+  {
+    INSTALL("INSTALL", COLOR_INSTALL, COLOR_FOREGROUND_DEFAULT, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/install_button_install.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/install_button_install_hover.png")),
+
+    INSTALLING("INSTALLING", COLOR_INSTALLING, COLOR_INSTALLING_FOREGROUND),
+
+    INSTALLED("LAUNCH", COLOR_LAUNCH, COLOR_FOREGROUND_DEFAULT, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/install_button_launch.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/install_button_launch_hover.png"));
+
+    public final Image icon;
+
+    public final Image hoverIcon;
+
+    public final String label;
+
+    public final Color backgroundColor;
+
+    public final Color foregroundColor;
+
+    State(final String label, final Color backgroundColor, final Color foregroundColor)
+    {
+      this(label, backgroundColor, foregroundColor, null, null);
+    }
+
+    State(final String label, final Color backgroundColor, final Color foregroundColor, final Image icon, final Image hoverIcon)
+    {
+      this.label = label;
+      this.backgroundColor = backgroundColor;
+      this.foregroundColor = foregroundColor;
+      this.icon = icon;
+      this.hoverIcon = hoverIcon;
+    }
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerApplication.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerApplication.java
index f503b0a..5fc5bc2 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerApplication.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerApplication.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
@@ -151,7 +152,7 @@
         {
           if (installerDialog[0] != null)
           {
-            ProxyPreferenceDialog proxyPreferenceDialog = new ProxyPreferenceDialog(installerDialog[0].getShell());
+            NetworkConnectionsDialog proxyPreferenceDialog = new NetworkConnectionsDialog(installerDialog[0].getShell());
             proxyPreferenceDialog.open();
           }
         }
@@ -189,7 +190,7 @@
 
       if (mode == Mode.ADVANCED)
       {
-        if (KeepInstallerDialog.canKeepInstaller())
+        if (InstallerUtil.canKeepInstaller())
         {
           Shell shell = new Shell(display);
           if (MessageDialog.openQuestion(shell, AbstractSetupDialog.SHELL_TEXT,
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerDialog.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerDialog.java
index 5428ccd..d431e25 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerDialog.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerDialog.java
@@ -147,7 +147,7 @@
                   return;
 
                 case 1:
-                  new ProxyPreferenceDialog(getShell()).open();
+                  new NetworkConnectionsDialog(getShell()).open();
                   installer.reloadIndex();
                   shell.getDisplay().asyncExec(checkIndex);
                   return;
@@ -180,7 +180,7 @@
       @Override
       public void widgetSelected(SelectionEvent e)
       {
-        Dialog dialog = new ProxyPreferenceDialog(getShell());
+        Dialog dialog = new NetworkConnectionsDialog(getShell());
         dialog.open();
       }
     });
@@ -192,7 +192,7 @@
       @Override
       public void widgetSelected(SelectionEvent e)
       {
-        Dialog dialog = new SSH2PreferenceDialog(getShell());
+        Dialog dialog = new NetworkSSH2Dialog(getShell());
         dialog.open();
       }
     });
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerUtil.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerUtil.java
new file mode 100644
index 0000000..7ecb50b
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallerUtil.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2014 Eike Stepper (Berlin, Germany) 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:
+ *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.util.IOUtil;
+import org.eclipse.oomph.util.OS;
+import org.eclipse.oomph.util.OomphPlugin.Preference;
+import org.eclipse.oomph.util.PropertiesUtil;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author Eike Stepper
+ */
+public final class InstallerUtil
+{
+  private static final Preference PREF_KEPT = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("kept");
+
+  private static String powerShell;
+
+  private InstallerUtil()
+  {
+  }
+
+  public static void createShortCut(String specialFolder, String target)
+  {
+    try
+    {
+      String powerShell = InstallerUtil.getPowerShell();
+      if (powerShell != null)
+      {
+        Runtime.getRuntime()
+            .exec(new String[] { powerShell, "-command",
+                "& {$linkPath = Join-Path ([Environment]::GetFolderPath('" + specialFolder + "')) 'Eclipse Installer.lnk'; $targetPath = '" + target
+                    + "'; $link = (New-Object -ComObject WScript.Shell).CreateShortcut( $linkpath ); $link.TargetPath = $targetPath; $link.Save()}" });
+      }
+    }
+    catch (IOException ex)
+    {
+      SetupInstallerPlugin.INSTANCE.log(ex);
+    }
+  }
+
+  public static void pinToTaskBar(String location, String launcherName)
+  {
+    try
+    {
+      String powerShell = InstallerUtil.getPowerShell();
+      if (powerShell != null)
+      {
+        Runtime.getRuntime().exec(new String[] { powerShell, "-command",
+            "& { (new-object -c shell.application).namespace('" + location + "').parsename('" + launcherName + "').invokeverb('taskbarpin') }" });
+      }
+    }
+    catch (IOException ex)
+    {
+      SetupInstallerPlugin.INSTANCE.log(ex);
+    }
+  }
+
+  protected void keepInstaller(String launcher, boolean startMenu, boolean desktop, boolean quickLaunch)
+  {
+  }
+
+  public static boolean canKeepInstaller()
+  {
+    if (!isInstallerKept() && OS.INSTANCE.isWin())
+    {
+      String launcher = InstallerApplication.getLauncher();
+      return launcher != null && launcher.startsWith(PropertiesUtil.TEMP_DIR);
+    }
+
+    return false;
+  }
+
+  public static String getPowerShell()
+  {
+    if (powerShell == null)
+    {
+      try
+      {
+        String systemRoot = System.getenv("SystemRoot");
+        if (systemRoot != null)
+        {
+          File system32 = new File(systemRoot, "system32");
+          if (system32.isDirectory())
+          {
+            File powerShellFolder = new File(system32, "WindowsPowerShell");
+            if (powerShellFolder.isDirectory())
+            {
+              File[] versions = powerShellFolder.listFiles();
+              if (versions != null)
+              {
+                for (File version : versions)
+                {
+                  try
+                  {
+                    File executable = new File(version, "powershell.exe");
+                    if (executable.isFile())
+                    {
+                      powerShell = executable.getAbsolutePath();
+                      break;
+                    }
+                  }
+                  catch (Exception ex)
+                  {
+                    //$FALL-THROUGH$
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+      catch (Exception ex)
+      {
+        //$FALL-THROUGH$
+      }
+    }
+
+    return powerShell;
+  }
+
+  public static void keepInstaller(String targetLocation, boolean startPermanentInstaller, String launcher, boolean startMenu, boolean desktop,
+      boolean quickLaunch)
+  {
+    File source = new File(launcher).getParentFile();
+    File target = new File(targetLocation);
+    IOUtil.copyTree(source, target, true);
+
+    String launcherName = new File(launcher).getName();
+    String permanentLauncher = new File(target, launcherName).getAbsolutePath();
+
+    if (startPermanentInstaller)
+    {
+      try
+      {
+        Runtime.getRuntime().exec(permanentLauncher);
+      }
+      catch (Exception ex)
+      {
+        SetupInstallerPlugin.INSTANCE.log(ex);
+      }
+    }
+    else
+    {
+      String url = target.toURI().toString();
+      OS.INSTANCE.openSystemBrowser(url);
+    }
+
+    if (startMenu)
+    {
+      createShortCut("Programs", permanentLauncher);
+    }
+
+    if (desktop)
+    {
+      createShortCut("Desktop", permanentLauncher);
+    }
+
+    if (quickLaunch)
+    {
+      pinToTaskBar(targetLocation, launcherName);
+    }
+
+    setKeepInstaller(true);
+  }
+
+  public static boolean isInstallerKept()
+  {
+    return PREF_KEPT.get(false);
+  }
+
+  public static void setKeepInstaller(boolean keep)
+  {
+    PREF_KEPT.set(keep);
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/KeepInstallerDialog.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/KeepInstallerDialog.java
index a52eaea..53b8de2 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/KeepInstallerDialog.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/KeepInstallerDialog.java
@@ -7,13 +7,11 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
 import org.eclipse.oomph.setup.ui.AbstractSetupDialog;
-import org.eclipse.oomph.util.IOUtil;
-import org.eclipse.oomph.util.OS;
-import org.eclipse.oomph.util.OomphPlugin.Preference;
 import org.eclipse.oomph.util.PropertiesUtil;
 import org.eclipse.oomph.util.StringUtil;
 
@@ -37,7 +35,6 @@
 import org.eclipse.swt.widgets.Text;
 
 import java.io.File;
-import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 
 /**
@@ -45,12 +42,6 @@
  */
 public final class KeepInstallerDialog extends AbstractSetupDialog
 {
-  private static final Preference PREF_KEPT = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("kept");
-
-  private static boolean kept = PREF_KEPT.get(false);
-
-  private static String powerShell;
-
   private final boolean startPermanentInstaller;
 
   private String location;
@@ -176,7 +167,7 @@
       }
     });
 
-    if (getPowerShell() != null)
+    if (InstallerUtil.getPowerShell() != null)
     {
       new Label(parent, SWT.NONE);
       startMenuButton = new Button(parent, SWT.CHECK);
@@ -234,7 +225,7 @@
           public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException
           {
             monitor.beginTask("Copying installer to " + location, IProgressMonitor.UNKNOWN);
-            keepInstaller(launcher, startMenu, desktop, quickLaunch);
+            InstallerUtil.keepInstaller(location, startPermanentInstaller, launcher, startMenu, desktop, quickLaunch);
             monitor.done();
           }
         });
@@ -251,144 +242,4 @@
 
     super.okPressed();
   }
-
-  protected void keepInstaller(String launcher, boolean startMenu, boolean desktop, boolean quickLaunch)
-  {
-    File source = new File(launcher).getParentFile();
-    File target = new File(location);
-    IOUtil.copyTree(source, target, true);
-
-    String launcherName = new File(launcher).getName();
-    String permanentLauncher = new File(target, launcherName).getAbsolutePath();
-
-    if (startPermanentInstaller)
-    {
-      try
-      {
-        Runtime.getRuntime().exec(permanentLauncher);
-      }
-      catch (Exception ex)
-      {
-        SetupInstallerPlugin.INSTANCE.log(ex);
-      }
-    }
-    else
-    {
-      String url = target.toURI().toString();
-      OS.INSTANCE.openSystemBrowser(url);
-    }
-
-    if (startMenu)
-    {
-      createShortCut("Programs", permanentLauncher);
-    }
-
-    if (desktop)
-    {
-      createShortCut("Desktop", permanentLauncher);
-    }
-
-    if (quickLaunch)
-    {
-      pinToTaskBar(location, launcherName);
-    }
-
-    kept = true;
-    PREF_KEPT.set(true);
-  }
-
-  private static void createShortCut(String specialFolder, String target)
-  {
-    try
-    {
-      String powerShell = getPowerShell();
-      if (powerShell != null)
-      {
-        Runtime.getRuntime()
-            .exec(new String[] { powerShell, "-command",
-                "& {$linkPath = Join-Path ([Environment]::GetFolderPath('" + specialFolder + "')) 'Eclipse Installer.lnk'; $targetPath = '" + target
-                    + "'; $link = (New-Object -ComObject WScript.Shell).CreateShortcut( $linkpath ); $link.TargetPath = $targetPath; $link.Save()}" });
-      }
-    }
-    catch (IOException ex)
-    {
-      SetupInstallerPlugin.INSTANCE.log(ex);
-    }
-  }
-
-  private static void pinToTaskBar(String location, String launcherName)
-  {
-    try
-    {
-      String powerShell = getPowerShell();
-      if (powerShell != null)
-      {
-        Runtime.getRuntime().exec(new String[] { powerShell, "-command",
-            "& { (new-object -c shell.application).namespace('" + location + "').parsename('" + launcherName + "').invokeverb('taskbarpin') }" });
-      }
-    }
-    catch (IOException ex)
-    {
-      SetupInstallerPlugin.INSTANCE.log(ex);
-    }
-  }
-
-  private static String getPowerShell()
-  {
-    if (powerShell == null)
-    {
-      try
-      {
-        String systemRoot = System.getenv("SystemRoot");
-        if (systemRoot != null)
-        {
-          File system32 = new File(systemRoot, "system32");
-          if (system32.isDirectory())
-          {
-            File powerShellFolder = new File(system32, "WindowsPowerShell");
-            if (powerShellFolder.isDirectory())
-            {
-              File[] versions = powerShellFolder.listFiles();
-              if (versions != null)
-              {
-                for (File version : versions)
-                {
-                  try
-                  {
-                    File executable = new File(version, "powershell.exe");
-                    if (executable.isFile())
-                    {
-                      powerShell = executable.getAbsolutePath();
-                      break;
-                    }
-                  }
-                  catch (Exception ex)
-                  {
-                    //$FALL-THROUGH$
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
-      catch (Exception ex)
-      {
-        //$FALL-THROUGH$
-      }
-    }
-
-    return powerShell;
-  }
-
-  public static boolean canKeepInstaller()
-  {
-    if (!kept && OS.INSTANCE.isWin())
-    {
-      String launcher = InstallerApplication.getLauncher();
-      return launcher != null && launcher.startsWith(PropertiesUtil.TEMP_DIR);
-    }
-
-    return false;
-  }
 }
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/MessageOverlay.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/MessageOverlay.java
new file mode 100644
index 0000000..7d66810
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/MessageOverlay.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.internal.ui.FlatButton;
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
+import org.eclipse.oomph.util.StringUtil;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @author Andreas Scharf
+ */
+public class MessageOverlay extends Shell implements ControlListener
+{
+  private static final int DEFAULT_AUTO_DISMISS_MILLIS = 4 * 1000;
+
+  private static final int MAX_MESSAGE_LENGTH = 175;
+
+  private static final int MAX_TOOLTIP_LINE_LENGTH = 60;
+
+  private final SimpleInstallerDialog dialog;
+
+  private final ControlRelocator controlRelocator;
+
+  private final boolean dismissAutomatically;
+
+  private Link link;
+
+  private boolean firstShown = true;
+
+  public MessageOverlay(SimpleInstallerDialog dialog, Type type, ControlRelocator controlRelocator, boolean dismissAutomatically)
+  {
+    this(dialog, type, controlRelocator, dismissAutomatically, null);
+  }
+
+  public MessageOverlay(SimpleInstallerDialog dialog, Type type, ControlRelocator controlRelocator, boolean dismissAutomatically, final Runnable action)
+  {
+    super(dialog, SWT.NO_TRIM);
+
+    if (type == null)
+    {
+      throw new IllegalArgumentException("Type must not be null!");
+    }
+
+    if (controlRelocator == null)
+    {
+      throw new IllegalArgumentException("Control relocator must not be null!");
+    }
+
+    this.dialog = dialog;
+    this.controlRelocator = controlRelocator;
+    this.dismissAutomatically = dismissAutomatically;
+
+    setBackground(type.backgroundColor);
+    setBackgroundMode(SWT.INHERIT_FORCE);
+
+    GridLayout layout = new GridLayout(2, false);
+    layout.marginWidth = 0;
+    layout.marginHeight = 0;
+    layout.marginLeft = 22;
+    layout.marginRight = 18;
+    layout.marginTop = 3;
+    layout.marginBottom = 3;
+    setLayout(layout);
+
+    link = new Link(this, SWT.NONE);
+    link.setLayoutData(GridDataFactory.swtDefaults().grab(true, true).create());
+    link.setFont(SimpleInstallerDialog.getDefaultFont());
+    link.setForeground(type.foregroundColor);
+    if (action != null)
+    {
+      link.addSelectionListener(new SelectionAdapter()
+      {
+        @Override
+        public void widgetSelected(SelectionEvent e)
+        {
+          action.run();
+          close();
+        }
+      });
+    }
+
+    dialog.addControlListener(this);
+
+    FlatButton closeButton = new ImageHoverButton(this, SWT.PUSH, type.closeImg, type.closeImgHover);
+    closeButton.setLayoutData(GridDataFactory.swtDefaults().align(SWT.END, SWT.BEGINNING).indent(0, 12).create());
+    closeButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        close();
+      }
+    });
+
+    // Initial bounds
+    controlRelocator.relocate(this);
+  }
+
+  @Override
+  public void setVisible(boolean visible)
+  {
+    super.setVisible(visible);
+
+    if (firstShown && visible && dismissAutomatically)
+    {
+      firstShown = false;
+
+      final Display display = getDisplay();
+      Thread dismissThread = new Thread(new Runnable()
+      {
+        public void run()
+        {
+          try
+          {
+            Thread.sleep(DEFAULT_AUTO_DISMISS_MILLIS);
+          }
+          catch (InterruptedException ex)
+          {
+            // Ignore.
+          }
+
+          display.asyncExec(new Runnable()
+          {
+            public void run()
+            {
+              dialog.clearMessage();
+            }
+          });
+        }
+      });
+
+      dismissThread.setDaemon(true);
+      dismissThread.start();
+    }
+  }
+
+  @Override
+  public void dispose()
+  {
+    getParent().removeControlListener(this);
+    super.dispose();
+  }
+
+  @Override
+  protected void checkSubclass()
+  {
+    // Nothing to do
+  }
+
+  public void setMessage(String message)
+  {
+    String tmp = message;
+    int maxMessageLength = MAX_MESSAGE_LENGTH;
+    if (message.length() > maxMessageLength)
+    {
+      tmp = StringUtil.shorten(message, maxMessageLength, false);
+
+      String wrapText = StringUtil.wrapText(message, MAX_TOOLTIP_LINE_LENGTH, true);
+      wrapText = ensureMaxLineLength(message, wrapText, MAX_TOOLTIP_LINE_LENGTH);
+
+      link.setToolTipText(wrapText);
+    }
+    else
+    {
+      link.setToolTipText(null);
+    }
+
+    link.setText(tmp);
+    layout();
+  }
+
+  private String ensureMaxLineLength(String originalText, String wrappedText, int maxLineLength)
+  {
+    String[] lines = wrappedText.contains(StringUtil.NL) ? wrappedText.split(StringUtil.NL) : new String[] { wrappedText };
+
+    for (String line : lines)
+    {
+      if (line.length() > maxLineLength)
+      {
+        wrappedText = StringUtil.wrapText(originalText, maxLineLength, false);
+        break;
+      }
+    }
+
+    return wrappedText;
+  }
+
+  public void controlResized(ControlEvent e)
+  {
+    if (!isDisposed())
+    {
+      controlRelocator.relocate(this);
+    }
+  }
+
+  public void controlMoved(ControlEvent e)
+  {
+    if (!isDisposed())
+    {
+      controlRelocator.relocate(this);
+    }
+  }
+
+  /**
+   * @author Andreas Scharf
+   */
+  public static interface ControlRelocator
+  {
+    public void relocate(Control control);
+  }
+
+  /**
+   * @author Andreas Scharf
+   */
+  public static enum Type
+  {
+    ERROR(SetupInstallerPlugin.getColor(249, 54, 50), SetupInstallerPlugin.COLOR_WHITE, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/close_message.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/close_message_hover.png")), SUCCESS(SetupInstallerPlugin.getColor(58, 195, 4),
+            SetupInstallerPlugin.COLOR_WHITE, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/close_message.png"),
+            SetupInstallerPlugin.INSTANCE.getSWTImage("simple/close_message_hover.png"));
+
+    public final Color backgroundColor;
+
+    public final Color foregroundColor;
+
+    public final Image closeImg;
+
+    public final Image closeImgHover;
+
+    private Type(Color backgroundColor, Color foregroundColor, Image closeImg, Image closeImgHover)
+    {
+      this.backgroundColor = backgroundColor;
+      this.foregroundColor = foregroundColor;
+      this.closeImg = closeImg;
+      this.closeImgHover = closeImgHover;
+    }
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/ProxyPreferenceDialog.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/NetworkConnectionsDialog.java
similarity index 93%
rename from plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/ProxyPreferenceDialog.java
rename to plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/NetworkConnectionsDialog.java
index 175bbff..77f444e 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/ProxyPreferenceDialog.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/NetworkConnectionsDialog.java
@@ -32,23 +32,27 @@
  * @author Eike Stepper
  */
 @SuppressWarnings("restriction")
-public class ProxyPreferenceDialog extends AbstractPreferenceDialog
+public class NetworkConnectionsDialog extends AbstractPreferenceDialog
 {
-  public ProxyPreferenceDialog(Shell parentShell)
+  public static final String TITLE = "Network Connections";
+
+  public static final String DESCRIPTION = "Adjust your network connection settings";
+
+  public NetworkConnectionsDialog(Shell parentShell)
   {
-    super(parentShell, "Network Proxy Settings");
+    super(parentShell, TITLE);
   }
 
   @Override
   protected String getShellText()
   {
-    return "Oomph Network Proxy Preferences";
+    return TITLE;
   }
 
   @Override
   protected String getDefaultMessage()
   {
-    return "Adjust your network proxy settings.";
+    return DESCRIPTION + ".";
   }
 
   @Override
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SSH2PreferenceDialog.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/NetworkSSH2Dialog.java
similarity index 84%
rename from plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SSH2PreferenceDialog.java
rename to plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/NetworkSSH2Dialog.java
index 8924fe4..547ea86 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SSH2PreferenceDialog.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/NetworkSSH2Dialog.java
@@ -20,23 +20,27 @@
  * @author Eike Stepper
  */
 @SuppressWarnings("restriction")
-public class SSH2PreferenceDialog extends AbstractPreferenceDialog
+public class NetworkSSH2Dialog extends AbstractPreferenceDialog
 {
-  public SSH2PreferenceDialog(Shell parentShell)
+  public static final String TITLE = "SSH2 Keys";
+
+  public static final String DESCRIPTION = "Adjust your SSH2 key settings";
+
+  public NetworkSSH2Dialog(Shell parentShell)
   {
-    super(parentShell, "SSH2 Settings");
+    super(parentShell, TITLE);
   }
 
   @Override
   protected String getShellText()
   {
-    return "Oomph SSH Preferences";
+    return TITLE;
   }
 
   @Override
   protected String getDefaultMessage()
   {
-    return "Adjust your SSH2 settings.";
+    return DESCRIPTION + ".";
   }
 
   @Override
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SetupInstallerPlugin.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SetupInstallerPlugin.java
index acc31bb..237ac36 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SetupInstallerPlugin.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SetupInstallerPlugin.java
@@ -7,17 +7,28 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
 import org.eclipse.oomph.setup.ui.SetupUIPlugin;
 import org.eclipse.oomph.ui.OomphUIPlugin;
+import org.eclipse.oomph.ui.UIUtil;
+import org.eclipse.oomph.util.PropertiesUtil;
 
 import org.eclipse.emf.common.ui.EclipseUIPlugin;
 import org.eclipse.emf.common.util.ResourceLocator;
 
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontData;
+
 import org.osgi.framework.BundleContext;
 
+import java.io.File;
+
 /**
  * @author Eike Stepper
  */
@@ -25,6 +36,16 @@
 {
   public static final SetupInstallerPlugin INSTANCE = new SetupInstallerPlugin();
 
+  public static final Color COLOR_WHITE = UIUtil.getDisplay().getSystemColor(SWT.COLOR_WHITE);
+
+  public static final Color COLOR_LIGHTEST_GRAY = getColor(245, 245, 245);
+
+  public static final Color COLOR_LABEL_FOREGROUND = getColor(85, 85, 85);
+
+  public static final String FONT_OPEN_SANS = "font-open-sans";
+
+  public static final String FONT_LABEL_DEFAULT = FONT_OPEN_SANS + ".label-default";
+
   private static Implementation plugin;
 
   public SetupInstallerPlugin()
@@ -52,6 +73,20 @@
     public void start(BundleContext context) throws Exception
     {
       super.start(context);
+      initializeFonts();
+    }
+
+    private void initializeFonts()
+    {
+      loadFont("/fonts/OpenSans-Regular.ttf");
+      JFaceResources.getFontRegistry().put(SetupInstallerPlugin.FONT_LABEL_DEFAULT, new FontData[] { new FontData("Open Sans", 9, SWT.BOLD) });
+    }
+
+    private boolean loadFont(String path)
+    {
+      File exportedFont = new File(PropertiesUtil.TEMP_DIR, new Path(path).lastSegment());
+      SetupInstallerPlugin.INSTANCE.exportResources(path, exportedFont);
+      return UIUtil.getDisplay().loadFont(exportedFont.toString());
     }
   }
 }
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleCheckbox.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleCheckbox.java
new file mode 100644
index 0000000..4a71b4a
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleCheckbox.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.internal.ui.FlatButton;
+
+import org.eclipse.emf.common.util.URI;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @author Andreas Scharf
+ */
+public class SimpleCheckbox extends FlatButton
+{
+  private static final Color COLOR_BACKGROUND = SetupInstallerPlugin.getColor(245, 245, 245);
+
+  private static final Color COLOR_HOVER = SetupInstallerPlugin.getColor(217, 217, 217);
+
+  private boolean checked;
+
+  public SimpleCheckbox(Composite parent)
+  {
+    super(parent, SWT.CHECK);
+    setImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/checkmark_checked.png"));
+
+    setIconTextGap(10);
+    addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        setChecked(!SimpleCheckbox.this.isChecked());
+      }
+    });
+    setFont(SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///9/bold")));
+    setForeground(SetupInstallerPlugin.COLOR_LABEL_FOREGROUND);
+  }
+
+  @Override
+  protected void drawImage(GC gc, int x, int y)
+  {
+    Image img = getImage();
+    Rectangle imgBounds = img.getBounds();
+
+    Color oldBG = gc.getBackground();
+    Color bgColor = isHover() ? COLOR_HOVER : COLOR_BACKGROUND;
+
+    gc.setBackground(bgColor);
+    gc.fillRoundRectangle(x, y, imgBounds.width, imgBounds.height, getCornerWidth(), getCornerWidth());
+    gc.setBackground(oldBG);
+
+    if (isChecked())
+    {
+      gc.drawImage(img, x, y);
+    }
+  }
+
+  public boolean isChecked()
+  {
+    return checked;
+  }
+
+  public void setChecked(boolean checked)
+  {
+    this.checked = checked;
+    redraw();
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallationLogPage.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallationLogPage.java
new file mode 100644
index 0000000..9e41e0c
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallationLogPage.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.ui.UIUtil;
+import org.eclipse.oomph.util.IOUtil;
+
+import org.eclipse.emf.common.util.URI;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Text;
+
+import java.io.File;
+
+/**
+ * @author Andreas Scharf
+ */
+public class SimpleInstallationLogPage extends SimpleInstallerPage
+{
+  private static final int SCROLL_SPEED = 8;
+
+  private ScrolledComposite scrolledComposite;
+
+  private Text text;
+
+  private File installationLogFile;
+
+  public SimpleInstallationLogPage(Composite parent, SimpleInstallerDialog dialog)
+  {
+    super(parent, dialog, true);
+  }
+
+  @Override
+  protected void createContent(Composite container)
+  {
+    GridLayout layout = new GridLayout(1, false);
+    layout.marginLeft = 17;
+    layout.marginRight = 11;
+    layout.marginTop = 39;
+    layout.marginBottom = 30;
+    layout.marginHeight = 0;
+    layout.marginWidth = 0;
+    layout.verticalSpacing = 0;
+
+    container.setLayout(layout);
+    container.setBackgroundMode(SWT.INHERIT_FORCE);
+    container.setBackground(SetupInstallerPlugin.COLOR_WHITE);
+
+    Label title = new Label(container, SWT.NONE);
+    title.setText("INSTALLATION LOG");
+    title.setForeground(UIUtil.COLOR_PURPLE);
+    title.setFont(SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///12/bold")));
+    title.setLayoutData(GridDataFactory.swtDefaults().create());
+
+    scrolledComposite = new ScrolledComposite(container, SWT.V_SCROLL | SWT.H_SCROLL);
+    scrolledComposite.setExpandHorizontal(true);
+    scrolledComposite.setExpandVertical(true);
+    text = new Text(scrolledComposite, SWT.MULTI | SWT.READ_ONLY);
+    scrolledComposite.setContent(text);
+    scrolledComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).indent(0, 20).create());
+
+    // Workaround for bug 93472 (Content of ScrolledComposite doesn't get scrolled by mousewheel).
+    // Setting the focus on the scroller doesn't work, that is why we forward the mouse wheel event.
+    text.addListener(SWT.MouseVerticalWheel, new Listener()
+    {
+      public void handleEvent(Event event)
+      {
+        int value = event.count * SCROLL_SPEED;
+        ScrollBar vbar = scrolledComposite.getVerticalBar();
+        vbar.setSelection(vbar.getSelection() - value);
+
+        Listener[] selectionListeners = vbar.getListeners(SWT.Selection);
+        for (Listener listener : selectionListeners)
+        {
+          listener.handleEvent(event);
+        }
+      }
+    });
+  }
+
+  public File getReadmeURI()
+  {
+    return installationLogFile;
+  }
+
+  public void setInstallationLogFile(File installationLogFile)
+  {
+    if (this.installationLogFile != installationLogFile)
+    {
+      this.installationLogFile = installationLogFile;
+
+      if (this.installationLogFile != null && this.installationLogFile.exists())
+      {
+        text.setText(readLog());
+      }
+      else
+      {
+        text.setText("No installation log available.");
+      }
+
+      scrolledComposite.setMinSize(text.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+    }
+  }
+
+  private String readLog()
+  {
+    return new String(IOUtil.readFile(installationLogFile));
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerDialog.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerDialog.java
index ebc1134..80dc5ed 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerDialog.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerDialog.java
@@ -7,52 +7,76 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
-import org.eclipse.oomph.internal.ui.AccessUtil;
+import org.eclipse.oomph.internal.ui.FlatButton;
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
+import org.eclipse.oomph.p2.core.BundlePool;
+import org.eclipse.oomph.p2.core.P2Util;
 import org.eclipse.oomph.p2.core.ProfileTransaction.Resolution;
+import org.eclipse.oomph.p2.internal.ui.AgentManagerDialog;
 import org.eclipse.oomph.setup.Product;
 import org.eclipse.oomph.setup.User;
 import org.eclipse.oomph.setup.internal.core.util.ECFURIHandlerImpl;
+import org.eclipse.oomph.setup.internal.installer.MessageOverlay.ControlRelocator;
+import org.eclipse.oomph.setup.ui.SetupUIPlugin;
 import org.eclipse.oomph.setup.ui.wizards.SetupWizard.Installer;
 import org.eclipse.oomph.ui.ErrorDialog;
-import org.eclipse.oomph.ui.ToolButton;
 import org.eclipse.oomph.ui.UIUtil;
 import org.eclipse.oomph.util.ExceptionHandler;
+import org.eclipse.oomph.util.IOExceptionWithCause;
 import org.eclipse.oomph.util.OS;
+import org.eclipse.oomph.util.OomphPlugin.BundleFile;
+import org.eclipse.oomph.util.OomphPlugin.Preference;
 
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.StackLayout;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseMoveListener;
 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.layout.FillLayout;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Label;
 
 import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Stack;
 
 /**
  * @author Eike Stepper
  */
 public final class SimpleInstallerDialog extends AbstractSimpleDialog implements InstallerUI
 {
-  public static final int MARGIN_WIDTH = 42;
+  private static final int INSTALLER_WIDTH = 523;
 
-  public static final int MARGIN_HEIGHT = 15;
+  private static final int INSTALLER_HEIGHT = 632;
 
-  private static final boolean CAPTURE = false;
+  private static final Preference PREF_POOL_ENABLED = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("poolEnabled");
+
+  private static Font defaultFont;
+
+  private static String css;
+
+  private static String pageTemplate;
+
+  private static String productTemplate;
 
   private final Installer installer;
 
+  private final Stack<SimpleInstallerPage> pageStack = new Stack<SimpleInstallerPage>();
+
   private Composite stack;
 
   private StackLayout stackLayout;
@@ -61,31 +85,207 @@
 
   private SimpleVariablePage variablePage;
 
-  private ToolButton updateButton;
+  private SimpleReadmePage readmePage;
+
+  private SimpleInstallationLogPage installationLogPage;
+
+  private SimpleKeepInstallerPage keepInstallerPage;
+
+  private SimpleInstallerMenu installerMenu;
+
+  private SimpleInstallerMenu.InstallerMenuItem updateInstallerItem;
+
+  private SimpleInstallerMenuButton menuButton;
+
+  private boolean poolEnabled;
+
+  private BundlePool pool;
 
   private Resolution updateResolution;
 
-  private ToolButton advancedButton;
+  private MessageOverlay currentMessage;
 
   public SimpleInstallerDialog(Display display, final Installer installer)
   {
-    super(display, OS.INSTANCE.isMac() ? SWT.TOOL : SWT.BORDER, 800, 600, MARGIN_WIDTH, MARGIN_HEIGHT);
+    super(display, OS.INSTANCE.isMac() ? SWT.TOOL : SWT.NO_TRIM, INSTALLER_WIDTH, INSTALLER_HEIGHT);
     this.installer = installer;
   }
 
   @Override
   protected void createUI(Composite titleComposite)
   {
-    if (CAPTURE)
+    poolEnabled = PREF_POOL_ENABLED.get(true);
+    enablePool(poolEnabled);
+
+    Composite exitMenuButtonContainer = new Composite(titleComposite, SWT.NONE);
+    exitMenuButtonContainer.setLayout(UIUtil.createGridLayout(1));
+    exitMenuButtonContainer.setLayoutData(GridDataFactory.swtDefaults().grab(false, true).align(SWT.CENTER, SWT.FILL).create());
+
+    FlatButton exitButton = new ImageHoverButton(exitMenuButtonContainer, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/exit.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/exit_hover.png"));
+    exitButton.setShowButtonDownState(false);
+    exitButton.setLayoutData(GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.BEGINNING).create());
+    exitButton.setToolTipText("Exit");
+    exitButton.addSelectionListener(new SelectionAdapter()
     {
-      captureDownloadButton();
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        exitSelected();
+      }
+    });
+
+    menuButton = new SimpleInstallerMenuButton(exitMenuButtonContainer);
+    menuButton.setLayoutData(GridDataFactory.swtDefaults().grab(false, true).align(SWT.CENTER, SWT.BEGINNING).indent(11, 0).create());
+    menuButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        toggleMenu();
+      }
+    });
+
+    stackLayout = new StackLayout();
+
+    stack = new Composite(this, SWT.NONE);
+    stack.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
+    stack.setLayout(stackLayout);
+
+    productPage = new SimpleProductPage(stack, this);
+    variablePage = new SimpleVariablePage(stack, this);
+    readmePage = new SimpleReadmePage(stack, this);
+    installationLogPage = new SimpleInstallationLogPage(stack, this);
+    keepInstallerPage = new SimpleKeepInstallerPage(stack, this);
+
+    switchToPage(productPage);
+
+    Display display = getDisplay();
+
+    Thread updateSearcher = new UpdateSearcher(display);
+    updateSearcher.start();
+
+    display.timerExec(500, new Runnable()
+    {
+      public void run()
+      {
+        installer.getResourceSet().getLoadOptions().put(ECFURIHandlerImpl.OPTION_CACHE_HANDLING, ECFURIHandlerImpl.CacheHandling.CACHE_WITHOUT_ETAG_CHECKING);
+        installer.loadIndex();
+      }
+    });
+
+    // Initialize menu
+    getInstallerMenu();
+
+    updateAvailable(false);
+  }
+
+  private void toggleMenu()
+  {
+    getInstallerMenu().setVisible(!getInstallerMenu().isVisible());
+  }
+
+  public Installer getInstaller()
+  {
+    return installer;
+  }
+
+  public void setButtonsEnabled(boolean enabled)
+  {
+    menuButton.setEnabled(enabled);
+  }
+
+  private void enablePool(boolean poolEnabled)
+  {
+    if (this.poolEnabled != poolEnabled)
+    {
+      this.poolEnabled = poolEnabled;
+      PREF_POOL_ENABLED.set(poolEnabled);
     }
 
-    updateButton = new ToolButton(titleComposite, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/update.png"), true);
-    updateButton.setLayoutData(new GridData(GridData.END, GridData.BEGINNING, false, false));
-    updateButton.setToolTipText("Update installer");
-    updateButton.setVisible(false);
-    updateButton.addSelectionListener(new SelectionAdapter()
+    if (poolEnabled)
+    {
+      pool = P2Util.getAgentManager().getDefaultBundlePool(SetupUIPlugin.INSTANCE.getSymbolicName());
+    }
+    else
+    {
+      pool = null;
+    }
+
+    // FIXME: Enabled/Disabled state for bundle pooling?
+    // if (poolButton != null)
+    // {
+    // poolButton.setImage(getBundlePoolImage());
+    // }
+  }
+
+  public BundlePool getPool()
+  {
+    return pool;
+  }
+
+  private SimpleInstallerMenu getInstallerMenu()
+  {
+    if (installerMenu == null)
+    {
+      installerMenu = createInstallerMenu();
+    }
+
+    return installerMenu;
+  }
+
+  private SimpleInstallerMenu createInstallerMenu()
+  {
+    SimpleInstallerMenu menu = new SimpleInstallerMenu(this);
+
+    SimpleInstallerMenu.InstallerMenuItem networkConnectionsItem = new SimpleInstallerMenu.InstallerMenuItem(menu);
+    networkConnectionsItem.setText(NetworkConnectionsDialog.TITLE.toUpperCase() + "...");
+    networkConnectionsItem.setToolTipText(NetworkConnectionsDialog.DESCRIPTION);
+    networkConnectionsItem.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        Dialog dialog = new NetworkConnectionsDialog(SimpleInstallerDialog.this);
+        dialog.open();
+      }
+    });
+
+    SimpleInstallerMenu.InstallerMenuItem bundlePoolsItem = new SimpleInstallerMenu.InstallerMenuItem(menu);
+    bundlePoolsItem.setText("BUNDLE POOLS...");
+    bundlePoolsItem.setToolTipText(AgentManagerDialog.MESSAGE);
+    bundlePoolsItem.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        manageBundlePools();
+      }
+    });
+
+    SimpleInstallerMenu.InstallerMenuItem ssh2KeysItem = new SimpleInstallerMenu.InstallerMenuItem(menu);
+    ssh2KeysItem.setText(NetworkSSH2Dialog.TITLE.toUpperCase() + "...");
+    ssh2KeysItem.setToolTipText(NetworkSSH2Dialog.DESCRIPTION);
+    ssh2KeysItem.setDividerVisible(false);
+    ssh2KeysItem.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        Dialog dialog = new NetworkSSH2Dialog(SimpleInstallerDialog.this);
+        dialog.open();
+      }
+    });
+
+    Label spacer1 = new Label(menu, SWT.NONE);
+    spacer1.setLayoutData(GridDataFactory.swtDefaults().hint(SWT.DEFAULT, 46).create());
+
+    updateInstallerItem = new SimpleInstallerMenu.InstallerMenuItem(menu);
+    updateInstallerItem.setDefaultImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/exclamation_circle.png"));
+    updateInstallerItem.setHoverImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/exclamation_circle_hover.png"));
+    updateInstallerItem.setText("UPDATE");
+    updateInstallerItem.setToolTipText("Install available updates");
+    updateInstallerItem.addSelectionListener(new SelectionAdapter()
     {
       @Override
       public void widgetSelected(SelectionEvent e)
@@ -111,10 +311,10 @@
       }
     });
 
-    advancedButton = new ToolButton(titleComposite, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/advanced.png"), true);
-    advancedButton.setLayoutData(new GridData(GridData.END, GridData.BEGINNING, false, false));
-    advancedButton.setToolTipText("Switch to advanced mode");
-    advancedButton.addSelectionListener(new SelectionAdapter()
+    SimpleInstallerMenu.InstallerMenuItem advancedModeItem = new SimpleInstallerMenu.InstallerMenuItem(menu);
+    advancedModeItem.setText("ADVANCED");
+    advancedModeItem.setToolTipText("Switch to advanced mode");
+    advancedModeItem.addSelectionListener(new SelectionAdapter()
     {
       @Override
       public void widgetSelected(SelectionEvent e)
@@ -124,10 +324,22 @@
       }
     });
 
-    ToolButton exitButton = new ToolButton(titleComposite, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/exit.png"), true);
-    exitButton.setLayoutData(new GridData(GridData.END, GridData.BEGINNING, false, false));
-    exitButton.setToolTipText("Exit");
-    exitButton.addSelectionListener(new SelectionAdapter()
+    SimpleInstallerMenu.InstallerMenuItem aboutItem = new SimpleInstallerMenu.InstallerMenuItem(menu);
+    aboutItem.setText("ABOUT");
+    aboutItem.setToolTipText("Show information about this installer");
+    aboutItem.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        showAbout();
+      }
+    });
+
+    SimpleInstallerMenu.InstallerMenuItem exitItem = new SimpleInstallerMenu.InstallerMenuItem(menu);
+    exitItem.setText("EXIT");
+    exitItem.setDividerVisible(false);
+    exitItem.addSelectionListener(new SelectionAdapter()
     {
       @Override
       public void widgetSelected(SelectionEvent e)
@@ -136,48 +348,74 @@
       }
     });
 
-    stackLayout = new StackLayout();
+    return menu;
+  }
 
-    stack = new Composite(this, SWT.NONE);
-    stack.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
-    stack.setLayout(stackLayout);
+  private void updateAvailable(boolean available)
+  {
+    menuButton.setNotificationVisible(available);
+    updateInstallerItem.setVisible(available);
+    installerMenu.layout();
+  }
 
-    productPage = new SimpleProductPage(stack, SWT.NONE, this);
-    variablePage = new SimpleVariablePage(stack, SWT.NONE, this);
+  private void manageBundlePools()
+  {
+    final boolean[] enabled = { poolEnabled };
 
-    stackLayout.topControl = productPage;
-    productPage.setFocus();
-
-    Display display = getDisplay();
-
-    Thread updateSearcher = new UpdateSearcher(display);
-    updateSearcher.start();
-
-    display.timerExec(500, new Runnable()
+    AgentManagerDialog dialog = new AgentManagerDialog(getShell())
     {
-      public void run()
+      @Override
+      protected void createUI(Composite parent)
       {
-        installer.getResourceSet().getLoadOptions().put(ECFURIHandlerImpl.OPTION_CACHE_HANDLING, ECFURIHandlerImpl.CacheHandling.CACHE_WITHOUT_ETAG_CHECKING);
-        installer.loadIndex();
+        final Button enabledButton = new Button(parent, SWT.CHECK);
+        enabledButton.setText("Enable shared bundle pool");
+        enabledButton.setSelection(poolEnabled);
+        enabledButton.addSelectionListener(new SelectionAdapter()
+        {
+          @Override
+          public void widgetSelected(SelectionEvent e)
+          {
+            enabled[0] = enabledButton.getSelection();
+            getComposite().setEnabled(enabled[0]);
+          }
+        });
+
+        new Label(parent, SWT.NONE);
+        super.createUI(parent);
+        getComposite().setEnabled(poolEnabled);
       }
-    });
-  }
 
-  public Installer getInstaller()
-  {
-    return installer;
-  }
+      @Override
+      protected void createButtonsForButtonBar(Composite parent)
+      {
+        super.createButtonsForButtonBar(parent);
+        Button button = getButton(IDialogConstants.OK_ID);
+        if (button != null)
+        {
+          button.setEnabled(false);
+        }
+      }
 
-  public void setButtonsEnabled(boolean enabled)
-  {
-    if (updateButton != null)
+      @Override
+      protected void elementChanged(Object element)
+      {
+        Button button = getButton(IDialogConstants.OK_ID);
+        if (button != null)
+        {
+          button.setEnabled(element instanceof BundlePool);
+        }
+      }
+    };
+
+    if (pool != null)
     {
-      updateButton.setEnabled(enabled);
+      dialog.setSelectedElement(pool);
     }
 
-    if (advancedButton != null)
+    if (dialog.open() == AgentManagerDialog.OK)
     {
-      advancedButton.setEnabled(enabled);
+      enablePool(enabled[0]);
+      pool = (BundlePool)dialog.getSelectedElement();
     }
   }
 
@@ -203,77 +441,210 @@
   public void productSelected(Product product)
   {
     variablePage.setProduct(product);
-    UIUtil.asyncExec(new Runnable()
-    {
-      public void run()
-      {
-        variablePage.setFocus();
-      }
-    });
-
-    stackLayout.topControl = variablePage;
-    stack.layout();
+    switchToPage(variablePage);
   }
 
   public void backSelected()
   {
+    if (pageStack.size() <= 1)
+    {
+      return;
+    }
+
     UIUtil.asyncExec(new Runnable()
     {
       public void run()
       {
-        productPage.reset(); // TODO Use JavaScript, so that the browser doesn't scroll to top!
-        productPage.setFocus();
-      }
-    });
+        SimpleInstallerPage currentPage = pageStack.pop();
 
-    stackLayout.topControl = productPage;
-    stack.layout();
-  }
-
-  private void captureDownloadButton()
-  {
-    final Shell captureShell = new Shell(this, SWT.NO_TRIM | SWT.MODELESS);
-    captureShell.setLayout(new FillLayout());
-
-    Image image = SetupInstallerPlugin.INSTANCE.getSWTImage("/download.png");
-
-    final ToolButton downloadActiveButton = new ToolButton(captureShell, SWT.RADIO, image, false);
-    downloadActiveButton.setBackground(WHITE);
-    downloadActiveButton.setSelection(true);
-
-    final ToolButton downloadHoverButton = new ToolButton(captureShell, SWT.PUSH, image, false);
-    downloadHoverButton.setBackground(WHITE);
-    downloadHoverButton.addMouseMoveListener(new MouseMoveListener()
-    {
-      public void mouseMove(MouseEvent e)
-      {
         try
         {
-          AccessUtil.save(new File("/develop/download_hover.png"), downloadHoverButton);
-          AccessUtil.save(new File("/develop/download_active.png"), downloadActiveButton);
+          currentPage.aboutToHide();
         }
         catch (Exception ex)
         {
-          ex.printStackTrace();
+          SetupInstallerPlugin.INSTANCE.log(ex);
         }
-        finally
-        {
-          // captureShell.dispose();
-        }
+
+        SimpleInstallerPage previousPage = pageStack.peek();
+
+        stackLayout.topControl = previousPage;
+        stack.layout();
+
+        previousPage.aboutToShow();
+        previousPage.setFocus();
       }
     });
+  }
 
-    captureShell.pack();
-    captureShell.open();
+  public void showMessage(String message, MessageOverlay.Type type, boolean dismissAutomatically)
+  {
+    showMessage(message, type, dismissAutomatically, null);
+  }
 
-    Point pt = getDisplay().map(downloadHoverButton, null, 10, 10);
-    downloadHoverButton.setFocus();
+  public void showMessage(String message, MessageOverlay.Type type, boolean dismissAutomatically, Runnable action)
+  {
+    clearMessage();
 
-    Event event = new Event();
-    event.type = SWT.MouseMove;
-    event.x = pt.x;
-    event.y = pt.y;
-    getDisplay().post(event);
+    currentMessage = new MessageOverlay(this, type, new ControlRelocator()
+    {
+      public void relocate(Control control)
+      {
+        Rectangle bounds = SimpleInstallerDialog.this.getBounds();
+        int x = bounds.x + 5;
+        int y = bounds.y + 24;
+
+        int width = bounds.width - 9;
+
+        // Depending on the current page, the height varies
+        int height = pageStack.peek() instanceof SimpleProductPage ? 87 : 70;
+        control.setBounds(x, y, width, height);
+      }
+    }, dismissAutomatically, action);
+
+    currentMessage.setMessage(message);
+    currentMessage.setVisible(true);
+  }
+
+  public void clearMessage()
+  {
+    if (currentMessage != null && !currentMessage.isDisposed())
+    {
+      if (!currentMessage.isDisposed())
+      {
+        currentMessage.close();
+      }
+
+      currentMessage = null;
+    }
+  }
+
+  public void showReadme(URI readmeURI)
+  {
+    readmePage.setReadmeURI(readmeURI);
+    switchToPage(readmePage);
+  }
+
+  public void showInstallationLog(File installationLogFile)
+  {
+    installationLogPage.setInstallationLogFile(installationLogFile);
+    switchToPage(installationLogPage);
+  }
+
+  public void showKeepInstaller()
+  {
+    switchToPage(keepInstallerPage);
+  }
+
+  private void switchToPage(final SimpleInstallerPage page)
+  {
+    if (page != null)
+    {
+      final SimpleInstallerPage currentPage = !pageStack.isEmpty() ? pageStack.peek() : null;
+      if (currentPage == null || currentPage != page)
+      {
+        pageStack.push(page);
+
+        UIUtil.asyncExec(new Runnable()
+        {
+          public void run()
+          {
+            if (currentPage != null)
+            {
+              currentPage.aboutToHide();
+            }
+
+            stackLayout.topControl = page;
+            stack.layout();
+
+            page.aboutToShow();
+            page.setFocus();
+          }
+        });
+      }
+    }
+  }
+
+  static Font getDefaultFont()
+  {
+    if (defaultFont == null)
+    {
+      defaultFont = JFaceResources.getFont(SetupInstallerPlugin.FONT_LABEL_DEFAULT);
+      if (defaultFont == null)
+      {
+        defaultFont = UIUtil.getDisplay().getSystemFont();
+      }
+    }
+
+    return defaultFont;
+  }
+
+  static String getCSS()
+  {
+    if (css == null)
+    {
+      try
+      {
+        css = readBundleResource("html/css/simpleInstaller.css");
+      }
+      catch (IOException ex)
+      {
+        SetupInstallerPlugin.INSTANCE.log(ex);
+      }
+    }
+
+    return css;
+  }
+
+  static String getPageTemplate()
+  {
+    if (pageTemplate == null)
+    {
+      try
+      {
+        pageTemplate = readBundleResource("html/PageTemplate.html");
+
+        // Embed CSS
+        pageTemplate = pageTemplate.replace("%INSTALLER_CSS%", SimpleInstallerDialog.getCSS());
+      }
+      catch (IOException ex)
+      {
+        SetupInstallerPlugin.INSTANCE.log(ex);
+      }
+    }
+
+    return pageTemplate;
+  }
+
+  static String getProductTemplate()
+  {
+    if (productTemplate == null)
+    {
+      try
+      {
+        productTemplate = readBundleResource("html/ProductTemplate.html");
+      }
+      catch (IOException ex)
+      {
+        SetupInstallerPlugin.INSTANCE.log(ex);
+      }
+    }
+
+    return productTemplate;
+  }
+
+  private static String readBundleResource(final String name) throws IOException
+  {
+    try
+    {
+      BundleFile root = SetupInstallerPlugin.INSTANCE.getRootFile();
+      BundleFile child = root.getChild(name);
+      return child.getContentsString();
+    }
+    catch (Exception ex)
+    {
+      throw new IOExceptionWithCause(ex);
+    }
   }
 
   /**
@@ -302,10 +673,7 @@
           {
             public void run()
             {
-              if (!updateButton.isDisposed())
-              {
-                updateButton.setVisible(true);
-              }
+              updateAvailable(true);
             }
           });
         }
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerMenu.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerMenu.java
new file mode 100644
index 0000000..76228ad
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerMenu.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.internal.ui.FlatButton;
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
+import org.eclipse.oomph.ui.UIUtil;
+
+import org.eclipse.emf.common.util.URI;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @author Andreas Scharf
+ */
+public class SimpleInstallerMenu extends Shell
+{
+  private static final int MENU_WIDTH = 340;
+
+  private static final int MENU_HEIGHT = 553;
+
+  public SimpleInstallerMenu(Shell parent)
+  {
+    super(parent, SWT.NO_TRIM);
+    setSize(MENU_WIDTH, MENU_HEIGHT);
+    setBackground(SetupInstallerPlugin.getColor(247, 148, 31));
+    setBackgroundMode(SWT.INHERIT_FORCE);
+
+    GridLayout layout = UIUtil.createGridLayout(1);
+    layout.marginHeight = 19;
+    layout.marginWidth = 28;
+    layout.verticalSpacing = 0;
+    setLayout(layout);
+
+    FlatButton closeButton = new ImageHoverButton(this, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/close.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/close_hover.png"));
+    closeButton.setLayoutData(GridDataFactory.swtDefaults().grab(true, false).align(SWT.END, SWT.BEGINNING).create());
+    closeButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        close();
+      }
+    });
+
+    hookListeners();
+  }
+
+  @Override
+  public void setVisible(boolean visible)
+  {
+    if (visible)
+    {
+      adjustPosition();
+    }
+
+    super.setVisible(visible);
+  }
+
+  @Override
+  public void close()
+  {
+    setVisible(false);
+  }
+
+  @Override
+  protected void checkSubclass()
+  {
+    // Subclassing is allowed.
+  }
+
+  private void hookListeners()
+  {
+    getParent().addControlListener(new ControlAdapter()
+    {
+      @Override
+      public void controlMoved(ControlEvent e)
+      {
+        adjustPosition();
+      }
+    });
+  }
+
+  private void adjustPosition()
+  {
+    Composite parent = getParent();
+    Rectangle bounds = parent.getBounds();
+
+    Point bottomRight = new Point(bounds.x + bounds.width, bounds.y + bounds.height);
+    parent.toDisplay(bottomRight);
+
+    Point size = getSize();
+    setBounds(bottomRight.x, bottomRight.y - size.y - 5, size.x, size.y);
+  }
+
+  /**
+   * @author Andreas Scharf
+   */
+  public static class InstallerMenuItem extends ImageHoverButton
+  {
+    private static final Font FONT = SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///13/bold"));
+
+    private Divider divider;
+
+    public InstallerMenuItem(final SimpleInstallerMenu menu)
+    {
+      super(menu, SWT.PUSH);
+
+      GridLayout layout = UIUtil.createGridLayout(1);
+      layout.marginWidth = 0;
+      layout.marginHeight = 0;
+
+      setLayout(layout);
+      setAlignment(SWT.LEFT);
+      setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 36).create());
+      setFont(FONT);
+      setForeground(SetupInstallerPlugin.COLOR_WHITE);
+
+      divider = new Divider(this, 1);
+      divider.setBackground(SetupInstallerPlugin.COLOR_WHITE);
+      divider.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.END).grab(true, true).create());
+
+      addSelectionListener(new SelectionAdapter()
+      {
+        @Override
+        public void widgetSelected(SelectionEvent e)
+        {
+          menu.close();
+        }
+      });
+    }
+
+    @Override
+    public void setVisible(boolean visible)
+    {
+      super.setVisible(visible);
+
+      Object data = getLayoutData();
+      if (data instanceof GridData)
+      {
+        ((GridData)data).exclude = !visible;
+
+        Composite parent = getParent();
+        if (parent != null)
+        {
+          parent.layout();
+        }
+      }
+    }
+
+    public boolean isdDividerVisible()
+    {
+      return !((GridData)divider.getLayoutData()).exclude;
+    }
+
+    public void setDividerVisible(boolean visible)
+    {
+      ((GridData)divider.getLayoutData()).exclude = !visible;
+      layout();
+    }
+
+    /**
+     * @author Andreas Scharf
+     */
+    private static final class Divider extends Composite implements PaintListener
+    {
+      private final int height;
+
+      public Divider(Composite parent, int height)
+      {
+        super(parent, SWT.NONE);
+        this.height = height;
+        addPaintListener(this);
+      }
+
+      @Override
+      public Point computeSize(int wHint, int hHint, boolean changed)
+      {
+        return new Point(wHint > 0 ? wHint : 0, height);
+      }
+
+      public void paintControl(PaintEvent e)
+      {
+        Rectangle clientArea = getClientArea();
+        e.gc.fillRectangle(clientArea.x, clientArea.y, clientArea.width, clientArea.height);
+      }
+    }
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerMenuButton.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerMenuButton.java
new file mode 100644
index 0000000..bca9283
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerMenuButton.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.internal.ui.FlatButton;
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * @author Andreas Scharf
+ */
+public class SimpleInstallerMenuButton extends Composite
+{
+  private static final int NOTIFICATION_X_OFFSET = 15;
+
+  // TODO We have some issues with transparency of the overlay
+  // if we move the overlay a bit down (e.g. by using a y-offset of -7)
+  // for example.
+  private static final int NOTIFICATION_Y_OFFSET = -9;
+
+  private final Label notificationOverlay;
+
+  private final FlatButton button;
+
+  public SimpleInstallerMenuButton(Composite parent)
+  {
+    super(parent, SWT.NONE);
+    setLayout(new FillLayout());
+
+    Composite container = new Composite(this, SWT.NONE);
+
+    notificationOverlay = new Label(container, SWT.NONE);
+    Image overlayImage = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/notification_overlay.png");
+    Rectangle overlayImageBounds = overlayImage.getBounds();
+    notificationOverlay.setImage(overlayImage);
+
+    int overlayX = notNegative(NOTIFICATION_X_OFFSET);
+    int overlayY = notNegative(NOTIFICATION_Y_OFFSET);
+
+    notificationOverlay.setBounds(overlayX, overlayY, overlayImageBounds.width, overlayImageBounds.height);
+    notificationOverlay.setVisible(false);
+
+    Image buttonImage = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/menu.png");
+    Image buttonHoverImage = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/menu_hover.png");
+    Image buttonDisabledImage = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/menu_disabled.png");
+    button = new ImageHoverButton(container, SWT.PUSH, buttonImage, buttonHoverImage, buttonDisabledImage);
+
+    // TODO As soon as the transparancy issues with the overlay are solved,
+    // we can re-enable the visualization of the button down state.
+    button.setShowButtonDownState(false);
+
+    int baseX = positive(NOTIFICATION_X_OFFSET);
+    int baseY = positive(NOTIFICATION_Y_OFFSET);
+
+    Rectangle baseBounds = buttonHoverImage.getBounds();
+    button.setBounds(baseX, baseY, baseBounds.width, baseBounds.height);
+
+    Rectangle unionBounds = notificationOverlay.getBounds().union(button.getBounds());
+    container.setSize(unionBounds.width, unionBounds.height);
+    setNotificationVisible(false);
+  }
+
+  @Override
+  public void setEnabled(boolean enabled)
+  {
+    super.setEnabled(enabled);
+    button.setEnabled(enabled);
+    notificationOverlay.setEnabled(enabled);
+  }
+
+  public void setNotificationVisible(boolean visible)
+  {
+    notificationOverlay.setVisible(visible);
+  }
+
+  public void addSelectionListener(SelectionListener listener)
+  {
+    button.addSelectionListener(listener);
+  }
+
+  public void removeSelectionListener(SelectionListener listener)
+  {
+    button.removeSelectionListener(listener);
+  }
+
+  private static int notNegative(int value)
+  {
+    return value >= 0 ? value : 0;
+  }
+
+  private static int positive(int value)
+  {
+    return value >= 0 ? 0 : -value;
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerPage.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerPage.java
index 17c809a..b286d5d 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerPage.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleInstallerPage.java
@@ -7,27 +7,31 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
+import org.eclipse.oomph.internal.ui.FlatButton;
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
 import org.eclipse.oomph.setup.ui.wizards.SetupWizard.Installer;
+import org.eclipse.oomph.ui.UIUtil;
 
 import org.eclipse.emf.common.util.URI;
 
+import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.MouseMoveListener;
-import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.RGB;
-import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
 
 /**
  * @author Eike Stepper
@@ -38,26 +42,146 @@
 
   public static final RGB ACTIVE_RGB = new RGB(196, 211, 254);
 
-  private static final URI FONT_URI = URI.createURI("font://Helvetica/14///");
+  public static final Color COLOR_PAGE_BORDER = SetupInstallerPlugin.getColor(238, 238, 238);
 
-  protected final Font font;
+  protected static final Font FONT_LABEL = SimpleInstallerDialog.getDefaultFont();
 
   protected final Installer installer;
 
   protected final SimpleInstallerDialog dialog;
 
-  public SimpleInstallerPage(final Composite parent, int style, final SimpleInstallerDialog dialog)
+  private final FlatButton backButton;
+
+  public SimpleInstallerPage(final Composite parent, final SimpleInstallerDialog dialog, boolean withBackButton)
   {
-    super(parent, style);
-    font = SetupInstallerPlugin.getFont(parent.getFont(), FONT_URI);
+    super(parent, SWT.NONE);
     installer = dialog.getInstaller();
     this.dialog = dialog;
+
+    GridLayout layout = new GridLayout(1, false);
+    layout.marginWidth = 3;
+    layout.marginHeight = 4;
+    layout.verticalSpacing = 0;
+    setLayout(layout);
+
+    Composite container = new Composite(this, SWT.NONE);
+    GridLayout containerLayout = UIUtil.createGridLayout(1);
+    containerLayout.verticalSpacing = 0;
+    container.setLayout(containerLayout);
+    container.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
+    container.setBackground(COLOR_PAGE_BORDER);
+
+    createContent(container);
+
+    if (withBackButton)
+    {
+      Composite buttonContainer = new Composite(this, SWT.NONE);
+      buttonContainer.setLayout(UIUtil.createGridLayout(1));
+      buttonContainer.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 33).create());
+      buttonContainer.setBackgroundMode(SWT.INHERIT_FORCE);
+      buttonContainer.setBackground(SetupInstallerPlugin.COLOR_WHITE);
+
+      backButton = new BackButton(buttonContainer);
+      backButton.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).indent(15, 0).create());
+      backButton.setToolTipText("Back");
+      backButton.addSelectionListener(new SelectionAdapter()
+      {
+        @Override
+        public void widgetSelected(SelectionEvent e)
+        {
+          dialog.backSelected();
+        }
+      });
+    }
+    else
+    {
+      backButton = null;
+    }
+  }
+
+  @Override
+  public void setEnabled(boolean enabled)
+  {
+    if (backButton != null)
+    {
+      backButton.setEnabled(enabled);
+    }
+  }
+
+  protected Text createTextField(Composite parent)
+  {
+    Composite textContainer = createInputFieldWrapper(parent, 0, 7, 0, 7);
+    applyComboOrTextStyle(textContainer);
+
+    Text textField = new Text(textContainer, SWT.NONE | SWT.SINGLE);
+    textField.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, true).create());
+    applyComboOrTextStyle(textField);
+
+    return textField;
+  }
+
+  protected CCombo createComboBox(Composite parent, int style)
+  {
+    Composite comboContainer = createInputFieldWrapper(parent, 0, 0, 0, 7);
+    applyComboOrTextStyle(comboContainer);
+
+    CCombo combo = new CCombo(comboContainer, style);
+    combo.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, true).create());
+    applyComboOrTextStyle(combo);
+
+    return combo;
+  }
+
+  private Composite createInputFieldWrapper(Composite parent, int marginTop, int marginRight, int marginBottom, int marginLeft)
+  {
+    GridLayout textContainerLayout = new GridLayout();
+    textContainerLayout.marginHeight = 0;
+    textContainerLayout.marginWidth = 0;
+    textContainerLayout.marginTop = marginTop;
+    textContainerLayout.marginRight = marginRight;
+    textContainerLayout.marginBottom = marginBottom;
+    textContainerLayout.marginLeft = marginLeft;
+
+    Composite textContainer = new Composite(parent, SWT.NONE);
+    textContainer.setLayout(textContainerLayout);
+    textContainer.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 30).create());
+    textContainer.setBackgroundMode(SWT.INHERIT_FORCE);
+    return textContainer;
+  }
+
+  protected abstract void createContent(Composite container);
+
+  public void aboutToShow()
+  {
+    // Subclasses may implement.
+  }
+
+  public void aboutToHide()
+  {
+    // Subclasses may implement.
+  }
+
+  protected void applyComboOrTextStyle(Control control)
+  {
+    control.setFont(SetupInstallerPlugin.getFont(FONT_LABEL, URI.createURI("font:///10/normal")));
+    control.setForeground(SetupInstallerPlugin.COLOR_LABEL_FOREGROUND);
+    control.setBackground(SetupInstallerPlugin.COLOR_LIGHTEST_GRAY);
+  }
+
+  protected Label createLabel(Composite parent, String text)
+  {
+    Label label = new Label(parent, SWT.NONE);
+    label.setLayoutData(GridDataFactory.swtDefaults().create());
+    label.setText(text);
+    label.setFont(FONT_LABEL);
+    label.setForeground(SetupInstallerPlugin.COLOR_LABEL_FOREGROUND);
+    return label;
   }
 
   @Override
   protected void checkSubclass()
   {
-    // Disable the check that prevents subclassing of SWT components.
+    // Allow subclassing.
   }
 
   public static String hex(RGB color)
@@ -77,123 +201,25 @@
   }
 
   /**
-   * @author Eike Stepper
+   * @author Andreas Scharf
    */
-  public static abstract class ImageButton extends Label implements MouseTrackListener, MouseMoveListener, MouseListener, SelectionListener
+  private static class BackButton extends ImageHoverButton
   {
-    private static final Color HOVER_COLOR = SetupInstallerPlugin.getColor(HOVER_RGB);
+    private static final Image ARROW_LEFT = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/arrow_left.png");
 
-    private static final Color ACTIVE_COLOR = SetupInstallerPlugin.getColor(ACTIVE_RGB);
+    private static final Image ARROW_LEFT_HOVER = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/arrow_left_hover.png");
 
-    private Color oldBackground;
+    private static final Image ARROW_LEFT_DISABLED = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/arrow_left_disabled.png");
 
-    private boolean mouseDown;
+    private static final Font FONT = SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///10/bold"));
 
-    public ImageButton(Composite parent, Image image)
+    public BackButton(Composite parent)
     {
-      super(parent, SWT.NONE);
-      setImage(image);
-
-      addMouseTrackListener(this);
-      addMouseMoveListener(this);
-      addMouseListener(this);
+      super(parent, SWT.PUSH, ARROW_LEFT, ARROW_LEFT_HOVER, ARROW_LEFT_DISABLED);
+      setIconTextGap(16);
+      setText("BACK");
+      setForeground(SetupInstallerPlugin.COLOR_LABEL_FOREGROUND);
+      setFont(FONT);
     }
-
-    public void mouseEnter(MouseEvent e)
-    {
-      if (oldBackground == null)
-      {
-        oldBackground = getBackground();
-      }
-
-      if (mouseDown)
-      {
-        setBackground(ACTIVE_COLOR);
-      }
-      else
-      {
-        setBackground(HOVER_COLOR);
-      }
-    }
-
-    public void mouseExit(MouseEvent e)
-    {
-      if (oldBackground != null)
-      {
-        setBackground(oldBackground);
-        oldBackground = null;
-      }
-    }
-
-    public void mouseHover(MouseEvent e)
-    {
-      // Do nothing.
-    }
-
-    public void mouseMove(MouseEvent e)
-    {
-      Rectangle bounds = getBounds();
-      bounds.x = 0;
-      bounds.y = 0;
-
-      if (bounds.contains(e.x, e.y))
-      {
-        if (oldBackground == null)
-        {
-          mouseEnter(null);
-        }
-      }
-      else
-      {
-        if (oldBackground != null)
-        {
-          mouseExit(null);
-        }
-      }
-    }
-
-    public void mouseDoubleClick(MouseEvent e)
-    {
-      // Do nothing.
-    }
-
-    public void mouseDown(MouseEvent e)
-    {
-      mouseDown = true;
-      setBackground(ACTIVE_COLOR);
-    }
-
-    public void mouseUp(MouseEvent e)
-    {
-      if (oldBackground != null)
-      {
-        setBackground(HOVER_COLOR);
-        widgetSelected();
-      }
-      else
-      {
-        mouseExit(null);
-      }
-
-      mouseDown = false;
-    }
-
-    public void widgetDefaultSelected(SelectionEvent e)
-    {
-      // Do nothing.
-    }
-
-    public void widgetSelected(SelectionEvent e)
-    {
-      widgetSelected();
-    }
-
-    @Override
-    protected void checkSubclass()
-    {
-      // Disable the check that prevents subclassing of SWT components.
-    }
-
-    protected abstract void widgetSelected();
   }
 }
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleKeepInstallerPage.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleKeepInstallerPage.java
new file mode 100644
index 0000000..a78383e
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleKeepInstallerPage.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2014 Eike Stepper (Berlin, Germany) 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:
+ *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.internal.ui.FlatButton;
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
+import org.eclipse.oomph.setup.internal.installer.MessageOverlay.Type;
+import org.eclipse.oomph.ui.UIUtil;
+import org.eclipse.oomph.util.PropertiesUtil;
+import org.eclipse.oomph.util.StringUtil;
+
+import org.eclipse.emf.common.util.URI;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * @author Eike Stepper
+ */
+public class SimpleKeepInstallerPage extends SimpleInstallerPage
+{
+  private String location;
+
+  private FlatButton applyButton;
+
+  private SimpleCheckbox startMenuButton;
+
+  private SimpleCheckbox desktopButton;
+
+  private SimpleCheckbox quickLaunchButton;
+
+  private boolean startPermanentInstaller;
+
+  public SimpleKeepInstallerPage(Composite parent, SimpleInstallerDialog dialog)
+  {
+    super(parent, dialog, true);
+  }
+
+  @Override
+  protected void createContent(Composite container)
+  {
+    GridLayout layout = new GridLayout(1, false);
+    layout.marginLeft = 17;
+    layout.marginRight = 11;
+    layout.marginTop = 39;
+    layout.marginBottom = 30;
+    layout.marginHeight = 0;
+    layout.marginWidth = 0;
+    layout.verticalSpacing = 0;
+
+    container.setLayout(layout);
+    container.setBackgroundMode(SWT.INHERIT_FORCE);
+    container.setBackground(SetupInstallerPlugin.COLOR_WHITE);
+
+    Label title = new Label(container, SWT.NONE);
+    title.setText("Keep Installer");
+    title.setForeground(UIUtil.COLOR_PURPLE);
+    title.setFont(SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///12/bold")));
+    title.setLayoutData(GridDataFactory.swtDefaults().create());
+
+    Label description = new Label(container, SWT.NONE);
+    description.setText("Copy the installer to a permanent location on your disk.");
+    description.setForeground(SetupInstallerPlugin.COLOR_LABEL_FOREGROUND);
+    description.setLayoutData(GridDataFactory.swtDefaults().indent(0, 10).create());
+
+    Composite varContainer = new Composite(container, SWT.NONE);
+    GridLayout varContainerLayout = new GridLayout(3, false);
+    varContainerLayout.marginWidth = 0;
+    varContainerLayout.marginHeight = 0;
+    varContainerLayout.verticalSpacing = 3;
+    varContainer.setLayout(varContainerLayout);
+    varContainer.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).indent(0, 48).create());
+
+    Label copyToLabel = createLabel(varContainer, "Copy to");
+    copyToLabel.setLayoutData(GridDataFactory.swtDefaults().hint(144, SWT.DEFAULT).create());
+
+    final Text locationText = createTextField(varContainer);
+    locationText.addModifyListener(new ModifyListener()
+    {
+      public void modifyText(ModifyEvent e)
+      {
+        location = locationText.getText();
+        String error = validate();
+
+        setErrorMessage(error);
+
+        applyButton.setEnabled(error == null && location.length() != 0);
+      }
+
+      private String validate()
+      {
+        if (location.length() == 0)
+        {
+          return null;
+        }
+
+        File folder = new File(location);
+        if (!folder.exists())
+        {
+          return null;
+        }
+
+        if (!folder.isDirectory())
+        {
+          return "Path is not a directory.";
+        }
+
+        if (!isEmpty(folder))
+        {
+          return "Directory is not empty.";
+        }
+
+        return null;
+      }
+
+      private boolean isEmpty(File folder)
+      {
+        File[] children = folder.listFiles();
+        return children == null || children.length == 0;
+      }
+    });
+
+    FlatButton folderButton = new ImageHoverButton(varContainer, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_hover.png"));
+    folderButton.setLayoutData(GridDataFactory.swtDefaults().indent(12, 0).create());
+    folderButton.setToolTipText("Browse...");
+    folderButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        FileDialog chooser = new FileDialog(dialog.getShell(), SWT.APPLICATION_MODAL | SWT.SAVE);
+        chooser.setText("Keep Installer");
+
+        if (!StringUtil.isEmpty(location))
+        {
+          final File file = new File(location).getAbsoluteFile();
+          chooser.setFilterPath(file.getParent());
+          chooser.setFileName(file.getName());
+        }
+
+        String dir = chooser.open();
+        if (dir != null)
+        {
+          locationText.setText(dir);
+        }
+      }
+    });
+
+    if (InstallerUtil.getPowerShell() != null)
+    {
+      new Label(varContainer, SWT.NONE);
+      startMenuButton = createCheckbox(varContainer, "create start menu entry");
+      startMenuButton.setChecked(true);
+
+      new Label(varContainer, SWT.NONE);
+      desktopButton = createCheckbox(varContainer, "create desktop shortcut");
+
+      new Label(varContainer, SWT.NONE);
+      quickLaunchButton = createCheckbox(varContainer, "pin to task bar");
+    }
+
+    new Label(varContainer, SWT.NONE);
+
+    applyButton = new FlatButton(varContainer, SWT.PUSH);
+    applyButton.setLayoutData(GridDataFactory.fillDefaults().indent(0, 43).hint(SWT.DEFAULT, 36).create());
+    applyButton.setText("APPLY");
+    applyButton.setBackground(SetupInstallerPlugin.getColor(50, 196, 0));
+    applyButton.setForeground(SetupInstallerPlugin.COLOR_WHITE);
+    applyButton.setFont(SetupInstallerPlugin.getFont(getFont(), URI.createURI("font:///14/bold")));
+    applyButton.setCornerWidth(10);
+    applyButton.setAlignment(SWT.CENTER);
+    applyButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        final String launcher = InstallerApplication.getLauncher();
+        if (launcher != null)
+        {
+          final boolean startMenu = startMenuButton == null ? false : startMenuButton.isChecked();
+          final boolean desktop = desktopButton == null ? false : desktopButton.isChecked();
+          final boolean quickLaunch = quickLaunchButton == null ? false : quickLaunchButton.isChecked();
+
+          ProgressMonitorDialog progressMonitorDialog = new ProgressMonitorDialog((Shell)getShell().getParent());
+
+          try
+          {
+            progressMonitorDialog.run(true, false, new IRunnableWithProgress()
+            {
+              public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException
+              {
+                monitor.beginTask("Copying installer to " + location, IProgressMonitor.UNKNOWN);
+                InstallerUtil.keepInstaller(location, startPermanentInstaller, launcher, startMenu, desktop, quickLaunch);
+                monitor.done();
+              }
+            });
+          }
+          catch (InterruptedException ex)
+          {
+            // Ignore.
+          }
+          catch (Exception ex)
+          {
+            SetupInstallerPlugin.INSTANCE.log(ex);
+          }
+        }
+      }
+    });
+
+    getShell().getDisplay().asyncExec(new Runnable()
+    {
+      public void run()
+      {
+        File home = new File(PropertiesUtil.USER_HOME);
+        for (int i = 1; i < Integer.MAX_VALUE; i++)
+        {
+          File folder = new File(home, "oomph" + (i > 1 ? i : ""));
+          if (!folder.exists())
+          {
+            String path = folder.getAbsolutePath();
+            locationText.setText(path);
+            locationText.setSelection(path.length());
+            return;
+          }
+        }
+      }
+    });
+  }
+
+  public void setStartPermanentInstaller(boolean startPermanentInstaller)
+  {
+    this.startPermanentInstaller = startPermanentInstaller;
+  }
+
+  private SimpleCheckbox createCheckbox(Composite parent, String text)
+  {
+    final SimpleCheckbox checkbox = new SimpleCheckbox(parent);
+    checkbox.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).span(2, 1).create());
+    checkbox.setText(text);
+    return checkbox;
+  }
+
+  private void setErrorMessage(String text)
+  {
+    if (text == null)
+    {
+      dialog.clearMessage();
+    }
+    else
+    {
+      dialog.showMessage(text, Type.ERROR, false);
+    }
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleProductPage.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleProductPage.java
index d086d57..05d82f8 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleProductPage.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleProductPage.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
@@ -20,7 +21,6 @@
 import org.eclipse.oomph.setup.ui.wizards.CatalogSelector;
 import org.eclipse.oomph.setup.ui.wizards.ProductPage;
 import org.eclipse.oomph.setup.ui.wizards.SetupWizard.IndexLoader;
-import org.eclipse.oomph.ui.SearchField;
 import org.eclipse.oomph.ui.SearchField.FilterHandler;
 import org.eclipse.oomph.ui.SpriteAnimator;
 import org.eclipse.oomph.ui.StackComposite;
@@ -36,6 +36,7 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.browser.Browser;
 import org.eclipse.swt.browser.LocationAdapter;
@@ -54,17 +55,11 @@
  */
 public class SimpleProductPage extends SimpleInstallerPage implements FilterHandler
 {
+  private static final int MAX_DESCRIPTION_LENGTH = 120;
+
   private static final String PRODUCT_PREFIX = "product://";
 
-  private static final String downloadImageURI = ProductPage.getImageURI(SetupInstallerPlugin.INSTANCE, "simple/download.png");
-
-  private static final String downloadHoverImageURI = ProductPage.getImageURI(SetupInstallerPlugin.INSTANCE, "simple/download_hover.png");
-
-  private static final String downloadActiveImageURI = ProductPage.getImageURI(SetupInstallerPlugin.INSTANCE, "simple/download_active.png");
-
-  private static final boolean FANCY = false; // OS.INSTANCE.isWin();
-
-  private SearchField searchField;
+  private SimpleSearchField searchField;
 
   private ToolBar buttonBar;
 
@@ -76,25 +71,22 @@
 
   private Browser browser;
 
-  public SimpleProductPage(final Composite parent, int style, final SimpleInstallerDialog dialog)
+  public SimpleProductPage(final Composite parent, final SimpleInstallerDialog dialog)
   {
-    super(parent, style, dialog);
+    super(parent, dialog, false);
+  }
 
-    GridLayout layout = UIUtil.createGridLayout(1);
-    layout.verticalSpacing = 20;
-    setLayout(layout);
-
+  @Override
+  protected void createContent(Composite container)
+  {
     GridLayout searchLayout = UIUtil.createGridLayout(2);
-    searchLayout.marginWidth = SimpleInstallerDialog.MARGIN_WIDTH;
+    searchLayout.horizontalSpacing = 0;
 
-    Composite searchComposite = new Composite(this, SWT.NONE);
+    Composite searchComposite = new Composite(container, SWT.NONE);
     searchComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
     searchComposite.setLayout(searchLayout);
 
-    GridData searchFieldData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
-    searchFieldData.widthHint = 350;
-
-    searchField = new SearchField(searchComposite, SimpleProductPage.this)
+    searchField = new SimpleSearchField(searchComposite, SimpleProductPage.this)
     {
       @Override
       protected void finishFilter()
@@ -102,17 +94,19 @@
         browser.setFocus();
       }
     };
-    searchField.setLayoutData(searchFieldData);
-    searchField.getFilterControl().setFont(font);
+
+    searchField.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).hint(SWT.DEFAULT, 34).create());
 
     buttonBar = new ToolBar(searchComposite, SWT.FLAT | SWT.RIGHT);
-    buttonBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+    buttonBar.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).exclude(true).create());
 
     CatalogManager catalogManager = installer.getCatalogManager();
     catalogSelector = new CatalogSelector(catalogManager, true);
 
-    stackComposite = new StackComposite(this, SWT.NONE);
-    stackComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+    stackComposite = new StackComposite(container, SWT.NONE);
+    stackComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).indent(0, 4).create());
+    stackComposite.setBackgroundMode(SWT.INHERIT_FORCE);
+    stackComposite.setBackground(SetupInstallerPlugin.COLOR_WHITE);
 
     final SpriteIndexLoader indexLoader = new SpriteIndexLoader(stackComposite);
 
@@ -176,6 +170,14 @@
   }
 
   @Override
+  public void aboutToHide()
+  {
+    super.aboutToHide();
+    reset(); // TODO Use JavaScript, so that the browser doesn't scroll to top!
+    setFocus();
+  }
+
+  @Override
   public boolean setFocus()
   {
     return browser.setFocus();
@@ -183,35 +185,13 @@
 
   public void handleFilter(String filter)
   {
-    String filterText = searchField.getFilterControl().getText();
+    String filterText = searchField.getFilterText();
     if (filterText.length() != 0)
     {
       filter = filterText;
     }
 
-    StringBuilder builder = new StringBuilder();
-    builder.append("<html><style TYPE=\"text/css\"><!-- ");
-    builder.append("table{width:100%; border:none; border-collapse:collapse}");
-    builder.append(".label{font-size:1.1em; font-weight:700}");
-    builder.append(".description{font-size:14px; color:#333}");
-    builder.append(".col{padding:10px; border-top:1px solid #bbbbbb; border-bottom:1px solid #bbbbbb}");
-    builder.append(".col1{text-align:center}");
-    builder.append(".col2{width:100%}");
-    builder.append(".col3{text-align:center}");
-    builder.append(".zebra{background-color:#fafafa}");
-    if (FANCY)
-    {
-      builder
-          .append("a.dl{background-image:url('" + downloadImageURI + "'); background-repeat:no-repeat; background-position:top left; width:57px; height:56px}");
-      builder.append("a.dl:hover{background-image:url('" + downloadHoverImageURI + "')}");
-      builder.append("a.dl:active{background-image:url('" + downloadActiveImageURI + "')}");
-    }
-    else
-    {
-      builder.append("img.dl{border-style:none}");
-    }
-
-    builder.append(" --></style><body style=\"margin:0px; overflow:auto; font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif\"><table>\n");
+    StringBuilder productsBuilder = new StringBuilder();
 
     boolean noFilter = StringUtil.isEmpty(filter);
     if (!noFilter)
@@ -219,7 +199,6 @@
       filter = filter.toLowerCase();
     }
 
-    boolean zebra = true;
     for (Scope scope : catalogSelector.getSelectedCatalogs())
     {
       if (scope instanceof ProductCatalog)
@@ -230,25 +209,16 @@
           if (!ProductPage.getValidProductVersions(product).isEmpty()
               && (noFilter || isFiltered(product.getName(), filter) || isFiltered(product.getLabel(), filter) || isFiltered(product.getDescription(), filter)))
           {
-            renderProduct(builder, product, zebra, downloadImageURI);
-            zebra = !zebra;
+            productsBuilder.append(renderProduct(product, false, true));
           }
         }
       }
     }
 
-    String html = getHtml(builder);
-
-    // try
-    // {
-    // IOUtil.writeUTF8(new File("/develop/products.html"), html);
-    // }
-    // catch (Exception ex)
-    // {
-    // ex.printStackTrace();
-    // }
-
-    browser.setText(html, true);
+    String productPageHTML = SimpleInstallerDialog.getProductTemplate();
+    String simpleInstallerHTML = SimpleInstallerDialog.getPageTemplate();
+    productPageHTML = simpleInstallerHTML.replace("%CONTENT%", productsBuilder.toString());
+    browser.setText(productPageHTML, true);
   }
 
   public void reset()
@@ -288,68 +258,9 @@
     }
   }
 
-  public static String getHtml(StringBuilder builder)
+  private static String removeLinks(String description)
   {
-    builder.append("</table></body></html>\n");
-    return builder.toString();
-  }
-
-  public static void renderProduct(StringBuilder builder, Product product, boolean zebra, String downloadImageURI)
-  {
-    String imageURI = ProductPage.getProductImageURI(product);
-
-    String description = product.getDescription();
-    if (description != null && downloadImageURI != null)
-    {
-      int dot = findFirstDot(description);
-      if (dot == -1)
-      {
-        description += ".";
-      }
-      else
-      {
-        description = description.substring(0, dot + 1);
-      }
-    }
-
-    String label = product.getLabel();
-    if (StringUtil.isEmpty(label))
-    {
-      label = product.getName();
-    }
-
-    builder.append("<tr class=\"row" + (zebra ? " zebra" : "") + "\">");
-
-    builder.append("<td class=\"col col1\"><img src=\"");
-    builder.append(imageURI);
-    builder.append("\" width=\"42\" height=\"42\"></img></td>");
-
-    builder.append("<td class=\"col col2\"><p class=\"label\">");
-    builder.append(label);
-    builder.append("</p>");
-
-    if (description != null)
-    {
-      builder.append("<p class=\"description\">");
-      builder.append(description);
-      builder.append("</p></td>");
-    }
-
-    if (downloadImageURI != null)
-    {
-      if (FANCY)
-      {
-        builder.append("<td class=\"col col3\"><a class=\"dl\" href=\"product://" + product.getProductCatalog().getName() + "/" + product.getName()
-            + "\" title=\"Select\"/></td>");
-      }
-      else
-      {
-        builder.append("<td class=\"col col3\"><a class=\"dl\" href=\"product://" + product.getProductCatalog().getName() + "/" + product.getName()
-            + "\" title=\"Select\"><img class=\"dl\" src=\"" + downloadImageURI + "\"/></a></td>");
-      }
-    }
-
-    builder.append("</tr>\n");
+    return description.replaceAll("</?a[^>]*>", "");
   }
 
   private static int findFirstDot(String description)
@@ -391,6 +302,66 @@
     return string.toLowerCase().contains(filter);
   }
 
+  public static String renderProduct(Product product, boolean large, boolean link)
+  {
+    String imageURI = ProductPage.getProductImageURI(product);
+
+    String label = product.getLabel();
+    if (StringUtil.isEmpty(label))
+    {
+      label = product.getName();
+    }
+
+    String description = product.getDescription();
+    if (description != null)
+    {
+      int dot = findFirstDot(description);
+      if (dot == -1)
+      {
+        description += ".";
+      }
+      else
+      {
+        description = description.substring(0, dot + 1);
+      }
+    }
+    else
+    {
+      // TODO: Empty string? Or something like "No description available"?
+      description = "";
+    }
+
+    String containerStyle = "productContainer";
+
+    if (!large)
+    {
+      description = StringUtil.shorten(description, MAX_DESCRIPTION_LENGTH, true);
+      description = removeLinks(description);
+      containerStyle += " noSelect";
+    }
+
+    String productHtml = SimpleInstallerDialog.getProductTemplate();
+
+    if (link)
+    {
+      String productLink = "product://" + product.getProductCatalog().getName() + "/" + product.getName();
+      productHtml = productHtml.replace("%PRODUCT_LINK%", productLink);
+
+      containerStyle += " productLink";
+    }
+
+    if (large)
+    {
+      containerStyle += " largeProduct";
+    }
+
+    productHtml = productHtml.replace("%PRODUCT_CONTAINER_STYLE%", containerStyle);
+    productHtml = productHtml.replace("%PRODUCT_ICON_SRC%", imageURI);
+    productHtml = productHtml.replace("%PRODUCT_TITLE%", label);
+    productHtml = productHtml.replace("%PRODUCT_DESCRIPTION%", description);
+    return productHtml;
+  }
+
   /**
    * @author Eike Stepper
    */
@@ -468,7 +439,7 @@
                       return;
 
                     case 1:
-                      new ProxyPreferenceDialog(getShell()).open();
+                      new NetworkConnectionsDialog(getShell()).open();
                       installer.reloadIndex();
                       return;
 
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleReadmePage.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleReadmePage.java
new file mode 100644
index 0000000..fddf28e
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleReadmePage.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.ui.UIUtil;
+
+import org.eclipse.emf.common.util.URI;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationAdapter;
+import org.eclipse.swt.browser.LocationEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * @author Andreas Scharf
+ */
+public class SimpleReadmePage extends SimpleInstallerPage
+{
+  private java.net.URI readmeURI;
+
+  private Browser browser;
+
+  public SimpleReadmePage(Composite parent, SimpleInstallerDialog dialog)
+  {
+    super(parent, dialog, true);
+  }
+
+  @Override
+  protected void createContent(Composite container)
+  {
+    GridLayout layout = new GridLayout(1, false);
+    layout.marginLeft = 17;
+    layout.marginRight = 11;
+    layout.marginTop = 39;
+    layout.marginBottom = 30;
+    layout.marginHeight = 0;
+    layout.marginWidth = 0;
+    layout.verticalSpacing = 0;
+
+    container.setLayout(layout);
+    container.setBackgroundMode(SWT.INHERIT_FORCE);
+    container.setBackground(SetupInstallerPlugin.COLOR_WHITE);
+
+    Label title = new Label(container, SWT.NONE);
+    title.setText("README");
+    title.setForeground(UIUtil.COLOR_PURPLE);
+    title.setFont(SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///12/bold")));
+    title.setLayoutData(GridDataFactory.swtDefaults().create());
+
+    browser = new Browser(container, SWT.NONE);
+    browser.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).indent(0, 20).create());
+    browser.addLocationListener(new LocationAdapter()
+    {
+      @Override
+      public void changed(LocationEvent event)
+      {
+        styleBrowser();
+      }
+    });
+  }
+
+  private void styleBrowser()
+  {
+    StringBuilder styleInjection = new StringBuilder();
+    styleInjection.append("var newStyle = document.createElement(\"style\");\n");
+    styleInjection.append("newStyle.setAttribute(\"type\", \"text/css\");\n");
+    styleInjection.append("newStyle.innerHTML = \"body{overflow-x:hidden}\"\n");
+    styleInjection.append("document.getElementsByTagName(\"head\")[0].appendChild(newStyle);\n");
+    browser.execute(styleInjection.toString());
+  }
+
+  public java.net.URI getReadmeURI()
+  {
+    return readmeURI;
+  }
+
+  public void setReadmeURI(java.net.URI readmeURI)
+  {
+    if (this.readmeURI != readmeURI)
+    {
+      this.readmeURI = readmeURI;
+
+      if (readmeURI != null)
+      {
+        browser.setUrl(readmeURI.toString());
+      }
+    }
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleSearchField.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleSearchField.java
new file mode 100644
index 0000000..c441f33
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleSearchField.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.setup.internal.installer;
+
+import org.eclipse.oomph.ui.SearchField.FilterHandler;
+import org.eclipse.oomph.ui.UIUtil;
+
+import org.eclipse.emf.common.util.URI;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @author Andreas Scharf
+ */
+public class SimpleSearchField extends Composite
+{
+  private Text searchField;
+
+  private Label searchLabel;
+
+  @SuppressWarnings("restriction")
+  public SimpleSearchField(final Composite parent, final FilterHandler filterHandler)
+  {
+    super(parent, SWT.NONE);
+    GridLayout layout = UIUtil.createGridLayout(2);
+    layout.horizontalSpacing = 0;
+    layout.marginLeft = 18;
+    layout.marginRight = 24;
+    setLayout(layout);
+
+    searchField = new Text(this, SWT.NONE);
+    searchField.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, true).create());
+    searchField.setMessage(org.eclipse.ui.internal.WorkbenchMessages.FilteredTree_FilterMessage);
+    searchField.setFont(SetupInstallerPlugin.getFont(getFont(), URI.createURI("font:///11/normal")));
+    searchField.addModifyListener(new ModifyListener()
+    {
+      public void modifyText(ModifyEvent e)
+      {
+        filterHandler.handleFilter(searchField.getText());
+      }
+    });
+
+    searchField.addTraverseListener(new TraverseListener()
+    {
+      public void keyTraversed(TraverseEvent e)
+      {
+        if (e.keyCode == SWT.ESC)
+        {
+          if (!"".equals(searchField.getText()))
+          {
+            searchField.setText("");
+            e.doit = false;
+          }
+        }
+      }
+    });
+
+    searchField.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent e)
+      {
+        if (e.keyCode == SWT.CR || e.keyCode == SWT.ARROW_DOWN)
+        {
+          finishFilter();
+          e.doit = false;
+        }
+      }
+    });
+
+    searchLabel = new Label(this, SWT.NONE);
+    searchLabel.setImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/search.png"));
+    searchLabel.setLayoutData(GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.CENTER).grab(false, true).create());
+    searchLabel.setBackground(null);
+    setBackground(UIUtil.getDisplay().getSystemColor(SWT.COLOR_WHITE));
+  }
+
+  public String getFilterText()
+  {
+    return searchField.getText();
+  }
+
+  @Override
+  public void setFont(Font font)
+  {
+    super.setFont(font);
+    searchField.setFont(font);
+  }
+
+  @Override
+  public void setBackground(Color color)
+  {
+    super.setBackground(color);
+    searchField.setBackground(color);
+    searchLabel.setBackground(color);
+  }
+
+  /**
+   * Subclasses may override.
+   */
+  protected void finishFilter()
+  {
+    // Do nothing.
+  }
+}
diff --git a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleVariablePage.java b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleVariablePage.java
index e145adb..b5cfbdb 100644
--- a/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleVariablePage.java
+++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/SimpleVariablePage.java
@@ -7,19 +7,21 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.setup.internal.installer;
 
 import org.eclipse.oomph.base.Annotation;
 import org.eclipse.oomph.base.util.BaseUtil;
 import org.eclipse.oomph.internal.setup.SetupPrompter;
+import org.eclipse.oomph.internal.ui.FlatButton;
+import org.eclipse.oomph.internal.ui.ImageCheckbox;
+import org.eclipse.oomph.internal.ui.ImageHoverButton;
 import org.eclipse.oomph.jreinfo.JRE;
 import org.eclipse.oomph.jreinfo.JREManager;
 import org.eclipse.oomph.jreinfo.ui.JREController;
 import org.eclipse.oomph.p2.core.AgentManager;
-import org.eclipse.oomph.p2.core.BundlePool;
 import org.eclipse.oomph.p2.core.P2Util;
-import org.eclipse.oomph.p2.internal.ui.AgentManagerDialog;
 import org.eclipse.oomph.setup.AnnotationConstants;
 import org.eclipse.oomph.setup.AttributeRule;
 import org.eclipse.oomph.setup.Installation;
@@ -36,6 +38,8 @@
 import org.eclipse.oomph.setup.VariableTask;
 import org.eclipse.oomph.setup.internal.core.SetupContext;
 import org.eclipse.oomph.setup.internal.core.SetupTaskPerformer;
+import org.eclipse.oomph.setup.internal.installer.InstallLaunchButton.State;
+import org.eclipse.oomph.setup.internal.installer.MessageOverlay.Type;
 import org.eclipse.oomph.setup.log.ProgressLog;
 import org.eclipse.oomph.setup.ui.AbstractSetupDialog;
 import org.eclipse.oomph.setup.ui.JREDownloadHandler;
@@ -45,7 +49,6 @@
 import org.eclipse.oomph.setup.ui.wizards.ProductPage;
 import org.eclipse.oomph.setup.ui.wizards.ProgressPage;
 import org.eclipse.oomph.ui.StackComposite;
-import org.eclipse.oomph.ui.ToolButton;
 import org.eclipse.oomph.ui.UIUtil;
 import org.eclipse.oomph.util.IOUtil;
 import org.eclipse.oomph.util.OS;
@@ -64,7 +67,7 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.equinox.p2.metadata.ILicense;
-import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.viewers.ArrayContentProvider;
 import org.eclipse.jface.viewers.ComboViewer;
 import org.eclipse.jface.viewers.LabelProvider;
@@ -83,19 +86,13 @@
 import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.DirectoryDialog;
 import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Link;
-import org.eclipse.swt.widgets.ProgressBar;
 import org.eclipse.swt.widgets.Text;
 
 import java.io.File;
 import java.security.cert.Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -107,19 +104,21 @@
  */
 public class SimpleVariablePage extends SimpleInstallerPage
 {
-  private static final Preference PREF_POOL_ENABLED = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("poolEnabled");
+  private static final String SETUP_LOG_FILE = OS.INSTANCE.getEclipseDir() + "/configuration/org.eclipse.oomph.setup/setup.log";
 
   private static final Preference PREF_INSTALL_ROOT = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("installRoot");
 
   private static final File FILE_INSTALL_ROOT = new File(SetupInstallerPlugin.INSTANCE.getUserLocation().toFile(), PREF_INSTALL_ROOT.key() + ".txt");
 
-  private static final String TEXT_LAUNCH = "Launch";
+  private static final String TEXT_README = "show readme file";
 
-  private static final String TEXT_README = "Show readme file";
+  private static final String TEXT_LOG = "show installation log";
 
-  private static final String TEXT_LOG = "Show installation log";
+  private static final String TEXT_KEEP = "keep installer";
 
-  private static final String TEXT_KEEP = "Keep installer";
+  private static final String MESSAGE_SUCCESS = "Installation completed successfully.";
+
+  private static final String MESSAGE_FAILURE = "Installation failed with an error.";
 
   private final Map<String, ProductVersion> productVersions = new HashMap<String, ProductVersion>();
 
@@ -129,19 +128,13 @@
 
   private String readmePath;
 
-  private BundlePool pool;
-
-  private boolean poolEnabled;
-
   private Browser browser;
 
-  private Composite versionComposite;
-
   private CCombo versionCombo;
 
-  private ToolButton bitness32Button;
+  private ImageCheckbox bitness32Button;
 
-  private ToolButton bitness64Button;
+  private ImageCheckbox bitness64Button;
 
   private JREController javaController;
 
@@ -149,13 +142,11 @@
 
   private ComboViewer javaViewer;
 
-  private ToolButton javaButton;
+  private FlatButton javaButton;
 
   private Text folderText;
 
-  private ToolButton folderButton;
-
-  private ToolButton poolButton;
+  private FlatButton folderButton;
 
   private String installRoot;
 
@@ -165,7 +156,7 @@
 
   private StackComposite installStack;
 
-  private ToolButton installButton;
+  private InstallLaunchButton installButton;
 
   private String installError;
 
@@ -175,34 +166,34 @@
 
   private SimpleProgress progress;
 
-  private ProgressBar progressBar;
+  private FlatButton cancelButton;
 
-  private Link progressLabel;
+  private FlatButton showReadmeButton;
 
-  private ToolButton cancelButton;
+  private FlatButton keepInstallerButton;
 
-  private ToolButton backButton;
+  private Composite afterInstallComposite;
 
-  public SimpleVariablePage(final Composite parent, int style, final SimpleInstallerDialog dialog)
+  private FlatButton showInstallLogButton;
+
+  private Composite errorComposite;
+
+  public SimpleVariablePage(final Composite parent, final SimpleInstallerDialog dialog)
   {
-    super(parent, style, dialog);
+    super(parent, dialog, true);
+  }
 
-    poolEnabled = PREF_POOL_ENABLED.get(true);
-    enablePool(poolEnabled);
-
-    GridLayout layout = UIUtil.createGridLayout(4);
-    layout.marginWidth = SimpleInstallerDialog.MARGIN_WIDTH;
-    layout.marginTop = 5;
-    layout.marginBottom = SimpleInstallerDialog.MARGIN_HEIGHT;
-    layout.horizontalSpacing = 5;
-    layout.verticalSpacing = 5;
-    setLayout(layout);
+  @Override
+  protected void createContent(Composite container)
+  {
+    container.setBackgroundMode(SWT.INHERIT_FORCE);
+    container.setBackground(SetupInstallerPlugin.COLOR_WHITE);
 
     // Row 1
-    GridData browserLayoutData = new GridData(SWT.FILL, SWT.FILL, true, false, layout.numColumns, 1);
-    browserLayoutData.heightHint = OS.INSTANCE.isLinux() ? 120 : 142;
+    GridData browserLayoutData = GridDataFactory.fillDefaults().grab(true, false).create();
+    browserLayoutData.heightHint = OS.INSTANCE.isLinux() ? 120 : 216;
 
-    Composite browserComposite = new Composite(this, SWT.BORDER);
+    Composite browserComposite = new Composite(container, SWT.NONE);
     browserComposite.setLayoutData(browserLayoutData);
     browserComposite.setLayout(new FillLayout());
 
@@ -221,19 +212,23 @@
       }
     });
 
-    // Row 2
-    new Label(this, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, layout.numColumns, 1));
+    Composite variablesComposite = new Composite(container, SWT.NONE);
+    GridLayout variablesLayout = new GridLayout(4, false);
+    variablesLayout.horizontalSpacing = 8;
+    variablesLayout.verticalSpacing = 3;
+    variablesLayout.marginLeft = 14;
+    variablesLayout.marginRight = 30;
+    variablesLayout.marginTop = 40;
+    variablesLayout.marginBottom = 0;
+    variablesLayout.marginHeight = 0;
+    variablesComposite.setLayout(variablesLayout);
+    variablesComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
 
     // Row 3
-    createLabel("Product Version ");
+    Label productVersionLabel = createLabel(variablesComposite, "Product Version");
+    productVersionLabel.setLayoutData(GridDataFactory.swtDefaults().hint(135, SWT.DEFAULT).create());
 
-    versionComposite = new Composite(this, SWT.NONE);
-    versionComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-    versionComposite.setLayout(UIUtil.createGridLayout(4));
-
-    versionCombo = new CCombo(versionComposite, SWT.BORDER | SWT.READ_ONLY);
-    versionCombo.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
-    versionCombo.setFont(font);
+    versionCombo = createComboBox(variablesComposite, SWT.READ_ONLY);
     versionCombo.addSelectionListener(new SelectionAdapter()
     {
       @Override
@@ -245,55 +240,51 @@
       }
     });
 
-    if (JREManager.BITNESS_CHANGEABLE)
+    bitness32Button = new ImageCheckbox(variablesComposite, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/32bit.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/32bit_hover.png"));
+    bitness32Button.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).indent(4, 0).hint(SWT.DEFAULT, 30).create());
+    bitness32Button.setChecked(false);
+    bitness32Button.setVisible(JREManager.BITNESS_CHANGEABLE);
+    bitness32Button.addSelectionListener(new SelectionAdapter()
     {
-      new Label(versionComposite, SWT.NONE);
-
-      bitness32Button = new ToolButton(versionComposite, SWT.RADIO, SetupInstallerPlugin.INSTANCE.getSWTImage("32bit.png"), true);
-      bitness32Button.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
-      bitness32Button.setSelection(false);
-      bitness32Button.addSelectionListener(new SelectionAdapter()
+      @Override
+      public void widgetSelected(SelectionEvent e)
       {
-        @Override
-        public void widgetSelected(SelectionEvent e)
-        {
-          bitness32Button.setSelection(true);
-          bitness64Button.setSelection(false);
-          javaController.setBitness(32);
-        }
-      });
+        bitness32Button.setChecked(true);
+        bitness64Button.setChecked(false);
+        javaController.setBitness(32);
+      }
+    });
 
-      bitness64Button = new ToolButton(versionComposite, SWT.RADIO, SetupInstallerPlugin.INSTANCE.getSWTImage("64bit.png"), true);
-      bitness64Button.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
-      bitness64Button.setSelection(true);
-      bitness64Button.addSelectionListener(new SelectionAdapter()
+    bitness64Button = new ImageCheckbox(variablesComposite, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/64bit.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/64bit_hover.png"));
+    bitness64Button.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).hint(SWT.DEFAULT, 30).create());
+    bitness64Button.setChecked(true);
+    bitness64Button.setVisible(JREManager.BITNESS_CHANGEABLE);
+    bitness64Button.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
       {
-        @Override
-        public void widgetSelected(SelectionEvent e)
-        {
-          bitness32Button.setSelection(false);
-          bitness64Button.setSelection(true);
-          javaController.setBitness(64);
-        }
-      });
-    }
-
-    new Label(this, SWT.NONE);
-    new Label(this, SWT.NONE);
+        bitness32Button.setChecked(false);
+        bitness64Button.setChecked(true);
+        javaController.setBitness(64);
+      }
+    });
 
     // Row 4
-    javaLabel = createLabel("Java VM ");
+    javaLabel = createLabel(variablesComposite, "Java VM");
 
-    CCombo javaCombo = new CCombo(this, SWT.BORDER | SWT.READ_ONLY);
-    javaCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-    javaCombo.setFont(font);
+    CCombo javaCombo = createComboBox(variablesComposite, SWT.READ_ONLY);
+    applyComboOrTextStyle(javaCombo);
 
     javaViewer = new ComboViewer(javaCombo);
     javaViewer.setContentProvider(new ArrayContentProvider());
     javaViewer.setLabelProvider(new LabelProvider());
 
-    javaButton = new ToolButton(this, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png"), false);
-    javaButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
+    javaButton = new ImageHoverButton(variablesComposite, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_hover.png"), SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_disabled.png"));
+    javaButton.setLayoutData(GridDataFactory.swtDefaults().indent(4, 0).create());
     javaButton.setToolTipText("Select Java VM...");
     javaButton.addSelectionListener(new SelectionAdapter()
     {
@@ -329,14 +320,12 @@
       }
     };
 
-    new Label(this, SWT.NONE);
+    new Label(variablesComposite, SWT.NONE);
 
     // Row 5
-    createLabel("Installation Folder ");
+    createLabel(variablesComposite, "Installation Folder");
 
-    folderText = new Text(this, SWT.BORDER);
-    folderText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-    folderText.setFont(font);
+    folderText = createTextField(variablesComposite);
     folderText.addModifyListener(new ModifyListener()
     {
       public void modifyText(ModifyEvent e)
@@ -346,8 +335,9 @@
       }
     });
 
-    folderButton = new ToolButton(this, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png"), false);
-    folderButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
+    folderButton = new ImageHoverButton(variablesComposite, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png"),
+        SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_hover.png"), SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_disabled.png"));
+    folderButton.setLayoutData(GridDataFactory.swtDefaults().indent(4, 0).create());
     folderButton.setToolTipText("Select installation folder...");
     folderButton.addSelectionListener(new SelectionAdapter()
     {
@@ -372,38 +362,24 @@
       }
     });
 
-    poolButton = new ToolButton(this, SWT.PUSH, getBundlePoolImage(), false);
-    poolButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
-    poolButton.setToolTipText("Configure bundle pool...");
-    poolButton.addSelectionListener(new SelectionAdapter()
-    {
-      @Override
-      public void widgetSelected(SelectionEvent e)
-      {
-        manageBundlePools();
-      }
-    });
+    new Label(variablesComposite, SWT.NONE);
+    new Label(variablesComposite, SWT.NONE);
 
-    // Row 6
-    backButton = new ToolButton(this, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/back.png"), true);
-    backButton.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, true, 1, 2));
-    backButton.setToolTipText("Back");
-    backButton.addSelectionListener(new SelectionAdapter()
-    {
-      @Override
-      public void widgetSelected(SelectionEvent e)
-      {
-        dialog.backSelected();
-      }
-    });
+    installButton = new InstallLaunchButton(variablesComposite);
+    installButton.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.BEGINNING).hint(SWT.DEFAULT, 36).indent(0, 32).create());
+    installButton.setCurrentState(InstallLaunchButton.State.INSTALL);
 
-    installStack = new StackComposite(this, SWT.NONE);
-    installStack.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+    new Label(variablesComposite, SWT.NONE);
+    new Label(variablesComposite, SWT.NONE);
 
-    cancelButton = new ToolButton(this, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/cancel.png"), false);
-    cancelButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
-    cancelButton.setToolTipText("Cancel");
-    cancelButton.setVisible(false);
+    installStack = new StackComposite(variablesComposite, SWT.NONE);
+    installStack.setLayoutData(GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.BEGINNING).span(4, 1).indent(60, 0).hint(SWT.DEFAULT, 72).create());
+
+    final Composite duringInstallContainer = new Composite(installStack, SWT.NONE);
+    duringInstallContainer.setLayout(UIUtil.createGridLayout(1));
+
+    // During installation.
+    cancelButton = createButton(duringInstallContainer, "Cancel Installation", "Cancel", SetupInstallerPlugin.INSTANCE.getSWTImage("simple/delete.png"));
     cancelButton.addSelectionListener(new SelectionAdapter()
     {
       @Override
@@ -413,18 +389,45 @@
       }
     });
 
-    new Label(this, SWT.NONE);
+    GridLayout afterInstallLayout = UIUtil.createGridLayout(1);
+    afterInstallLayout.verticalSpacing = 3;
 
-    installButton = new ToolButton(installStack, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/download_small.png"), false);
+    afterInstallComposite = new Composite(installStack, SWT.NONE);
+    afterInstallComposite.setLayout(afterInstallLayout);
 
-    final Composite progressComposite = new Composite(installStack, SWT.NONE);
-    progressComposite.setLayout(UIUtil.createGridLayout(1));
+    showReadmeButton = createButton(afterInstallComposite, TEXT_README, null, null);
+    showReadmeButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        if (readmePath != null)
+        {
+          java.net.URI readmeURI = new File(installFolder, OS.INSTANCE.getEclipseDir() + "/" + readmePath).toURI();
+          dialog.showReadme(readmeURI);
+        }
+      }
+    });
 
-    GridData layoutData2 = new GridData(SWT.FILL, SWT.CENTER, true, true);
-    layoutData2.heightHint = 28;
+    showInstallLogButton = createButton(afterInstallComposite, TEXT_LOG, null, null);
+    showInstallLogButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        openInstallLog();
+      }
+    });
 
-    progressBar = new ProgressBar(progressComposite, SWT.NONE);
-    progressBar.setLayoutData(layoutData2);
+    keepInstallerButton = createButton(afterInstallComposite, TEXT_KEEP, null, null);
+    keepInstallerButton.addSelectionListener(new SelectionAdapter()
+    {
+      @Override
+      public void widgetSelected(SelectionEvent e)
+      {
+        dialog.showKeepInstaller();
+      }
+    });
 
     installButton.addSelectionListener(new SelectionAdapter()
     {
@@ -437,65 +440,59 @@
         }
         else
         {
+          dialog.clearMessage();
           dialog.setButtonsEnabled(false);
+
           setEnabled(false);
 
-          installButton.setImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/download_small.png"));
-          progressBar.setSelection(0);
-          progressLabel.setForeground(null);
-          cancelButton.setVisible(true);
+          installButton.setCurrentState(State.INSTALLING);
+          installButton.setProgress(0);
 
-          installStack.setTopControl(progressComposite);
+          installStack.setTopControl(duringInstallContainer);
+          installStack.setVisible(true);
+          layout(true, true);
 
           install();
         }
       }
     });
 
-    installStack.setTopControl(installButton);
+    GridLayout errorLayout = UIUtil.createGridLayout(1);
+    errorLayout.verticalSpacing = 0;
 
-    // Row 7
-    progressLabel = new Link(this, SWT.WRAP);
-    progressLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
-    progressLabel.setFont(SetupInstallerPlugin.getFont(font, URI.createURI("font:///9/bold")));
-    progressLabel.addSelectionListener(new SelectionAdapter()
+    errorComposite = new Composite(installStack, SWT.NONE);
+    errorComposite.setLayout(errorLayout);
+
+    // Just for debugging
+    // installStack.setVisible(true);
+    // installStack.setTopControl(errorComposite);
+    // installButton.setProgress(0.98f);
+    // installButton.setCurrentState(InstallLaunchButton.State.INSTALLING);
+    // installButton.setEnabled(false);
+  }
+
+  private FlatButton createButton(Composite parent, String text, String toolTip, Image icon)
+  {
+    FlatButton button = new FlatButton(parent, SWT.PUSH);
+    button.setBackground(SetupInstallerPlugin.COLOR_LIGHTEST_GRAY);
+    button.setText(text);
+    button.setCornerWidth(10);
+    button.setAlignment(SWT.CENTER);
+    button.setFont(SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///10/normal")));
+    button.setLayoutData(GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.BEGINNING).grab(false, false).hint(232, 22).create());
+    button.setForeground(SetupInstallerPlugin.COLOR_LABEL_FOREGROUND);
+
+    if (icon != null)
     {
-      @Override
-      public void widgetSelected(SelectionEvent e)
-      {
-        if (TEXT_LAUNCH.equals(e.text))
-        {
-          launchProduct();
-          return;
-        }
+      button.setImage(icon);
+    }
 
-        if (TEXT_README.equals(e.text))
-        {
-          if (readmePath != null)
-          {
-            String url = new File(installFolder, OS.INSTANCE.getEclipseDir() + "/" + readmePath).toURI().toString();
-            OS.INSTANCE.openSystemBrowser(url);
-          }
-        }
-        else if (TEXT_LOG.equals(e.text))
-        {
-          String url = new File(installFolder, OS.INSTANCE.getEclipseDir() + "/configuration/org.eclipse.oomph.setup/setup.log").toURI().toString();
-          OS.INSTANCE.openSystemBrowser(url);
-        }
-        else if (TEXT_KEEP.equals(e.text))
-        {
-          new KeepInstallerDialog(getShell(), false).open();
-        }
+    if (toolTip != null)
+    {
+      button.setToolTipText(toolTip);
+    }
 
-        installButton.setFocus();
-      }
-    });
-
-    List<Control> tabList = new ArrayList<Control>(Arrays.asList(getTabList()));
-    tabList.remove(browserComposite);
-    tabList.remove(backButton);
-    tabList.add(backButton);
-    setTabList(tabList.toArray(new Control[tabList.size()]));
+    return button;
   }
 
   protected void productVersionSelected(ProductVersion productVersion)
@@ -521,17 +518,10 @@
   {
     this.product = product;
 
-    StringBuilder builder = new StringBuilder();
-    builder.append("<html><style TYPE=\"text/css\"><!-- ");
-    builder.append("table{border:none; border-collapse:collapse}");
-    builder.append(".label{font-size:1.1em; font-weight:700}");
-    builder.append(".description{font-size:14px; color:#333}");
-    builder.append(".col1{padding:10px; width:64px; text-align:center; vertical-align:top}");
-    builder.append(
-        " --></style><body style=\"background-color:#fafafa; overflow:auto; margin:10px; font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif\"><table>\n");
+    String html = SimpleInstallerDialog.getPageTemplate();
+    html = html.replace("%CONTENT%", SimpleProductPage.renderProduct(product, true, false));
 
-    SimpleProductPage.renderProduct(builder, product, true, null);
-    browser.setText(SimpleProductPage.getHtml(builder), true);
+    browser.setText(html, true);
 
     productVersions.clear();
     versionCombo.removeAll();
@@ -564,12 +554,6 @@
       ++i;
     }
 
-    versionCombo.pack();
-    Point size = versionCombo.getSize();
-    size.x += 10;
-    versionCombo.setSize(size);
-    versionComposite.layout();
-
     versionCombo.select(selection);
     versionCombo.setSelection(new Point(0, 0));
     productVersionSelected(defaultProductVersion);
@@ -577,18 +561,18 @@
     installFolder = getDefaultInstallationFolder();
     setFolderText(installFolder);
 
-    installStack.setTopControl(installButton);
-    installButton.setImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/download_small.png"));
-    installButton.setToolTipText("Install");
+    installButton.setCurrentState(State.INSTALL);
+
+    installStack.setVisible(false);
     installed = false;
 
-    progressLabel.setText("");
     setEnabled(true);
   }
 
   @Override
   public void setEnabled(boolean enabled)
   {
+    super.setEnabled(enabled);
     versionCombo.setEnabled(enabled);
 
     if (JREManager.BITNESS_CHANGEABLE)
@@ -605,8 +589,6 @@
 
     folderText.setEnabled(enabled);
     folderButton.setEnabled(enabled);
-    poolButton.setEnabled(enabled);
-    backButton.setEnabled(enabled);
   }
 
   public boolean refreshJREs()
@@ -670,104 +652,6 @@
     throw new IllegalStateException("User home is full");
   }
 
-  private Image getBundlePoolImage()
-  {
-    return SetupInstallerPlugin.INSTANCE.getSWTImage("simple/bundle_pool_" + (poolEnabled ? "enabled" : "disabled") + ".png");
-  }
-
-  private void enablePool(boolean poolEnabled)
-  {
-    if (this.poolEnabled != poolEnabled)
-    {
-      this.poolEnabled = poolEnabled;
-      PREF_POOL_ENABLED.set(poolEnabled);
-    }
-
-    if (poolEnabled)
-    {
-      pool = P2Util.getAgentManager().getDefaultBundlePool(SetupUIPlugin.INSTANCE.getSymbolicName());
-    }
-    else
-    {
-      pool = null;
-    }
-
-    if (poolButton != null)
-    {
-      poolButton.setImage(getBundlePoolImage());
-    }
-  }
-
-  private Label createLabel(String text)
-  {
-    Label label = new Label(this, SWT.RIGHT);
-    label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
-    label.setText(text);
-    label.setFont(font);
-    return label;
-  }
-
-  private void manageBundlePools()
-  {
-    final boolean[] enabled = { poolEnabled };
-
-    AgentManagerDialog dialog = new AgentManagerDialog(getShell())
-    {
-      @Override
-      protected void createUI(Composite parent)
-      {
-        final Button enabledButton = new Button(parent, SWT.CHECK);
-        enabledButton.setText("Enable shared bundle pool");
-        enabledButton.setSelection(poolEnabled);
-        enabledButton.addSelectionListener(new SelectionAdapter()
-        {
-          @Override
-          public void widgetSelected(SelectionEvent e)
-          {
-            enabled[0] = enabledButton.getSelection();
-            getComposite().setEnabled(enabled[0]);
-          }
-        });
-
-        new Label(parent, SWT.NONE);
-        super.createUI(parent);
-        getComposite().setEnabled(poolEnabled);
-      }
-
-      @Override
-      protected void createButtonsForButtonBar(Composite parent)
-      {
-        super.createButtonsForButtonBar(parent);
-        Button button = getButton(IDialogConstants.OK_ID);
-        if (button != null)
-        {
-          button.setEnabled(false);
-        }
-      }
-
-      @Override
-      protected void elementChanged(Object element)
-      {
-        Button button = getButton(IDialogConstants.OK_ID);
-        if (button != null)
-        {
-          button.setEnabled(element instanceof BundlePool);
-        }
-      }
-    };
-
-    if (pool != null)
-    {
-      dialog.setSelectedElement(pool);
-    }
-
-    if (dialog.open() == AgentManagerDialog.OK)
-    {
-      enablePool(enabled[0]);
-      pool = (BundlePool)dialog.getSelectedElement();
-    }
-  }
-
   private void install()
   {
     installThread = new Thread()
@@ -796,11 +680,7 @@
           if (!progress.isCanceled())
           {
             SetupInstallerPlugin.INSTANCE.log(ex);
-            installError = ex.getMessage();
-            if (StringUtil.isEmpty(installError))
-            {
-              installError = ex.getClass().getName();
-            }
+            installError = MESSAGE_FAILURE;
           }
         }
         finally
@@ -837,10 +717,10 @@
 
   private void installPerform() throws Exception
   {
-    if (pool != null)
+    if (dialog.getPool() != null)
     {
-      P2Util.getAgentManager().setDefaultBundlePool(SetupUIPlugin.INSTANCE.getSymbolicName(), pool);
-      System.setProperty(AgentManager.PROP_BUNDLE_POOL_LOCATION, pool.getLocation().getAbsolutePath());
+      P2Util.getAgentManager().setDefaultBundlePool(SetupUIPlugin.INSTANCE.getSymbolicName(), dialog.getPool());
+      System.setProperty(AgentManager.PROP_BUNDLE_POOL_LOCATION, dialog.getPool().getLocation().getAbsolutePath());
     }
     else
     {
@@ -912,26 +792,23 @@
     dialog.setButtonsEnabled(true);
     setEnabled(true);
 
-    progressLabel.setForeground(getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
-    progressLabel.setText("Installation canceled");
-
-    cancelButton.setVisible(false);
-    installStack.setTopControl(installButton);
+    installButton.setCurrentState(State.INSTALL);
+    installStack.setVisible(false);
   }
 
   private void installFinished()
   {
     readmePath = null;
-    String message;
 
     if (installError == null)
     {
       installed = true;
-      installButton.setImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/launch.png"));
-      installButton.setToolTipText("Launch");
-      progressLabel.setForeground(getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN));
 
-      message = "Installation finished successfully: <a>" + TEXT_LAUNCH + "</a>\n\n";
+      installButton.setCurrentState(State.INSTALLED);
+      installButton.setToolTipText("Launch");
+
+      showInstallLogButton.setParent(afterInstallComposite);
+      keepInstallerButton.setVisible(InstallerUtil.canKeepInstaller());
 
       Scope scope = selectedProductVersion;
       while (scope != null)
@@ -942,7 +819,7 @@
           readmePath = annotation.getDetails().get(AnnotationConstants.KEY_README_PATH);
           if (readmePath != null)
           {
-            message += "<a>" + TEXT_README + "</a>\n";
+            showReadmeButton.setEnabled(true);
             break;
           }
         }
@@ -950,30 +827,53 @@
         scope = scope.getParentScope();
       }
 
-      message += "<a>" + TEXT_LOG + "</a>";
-
-      if (KeepInstallerDialog.canKeepInstaller())
-      {
-        message += "\n<a>" + TEXT_KEEP + "</a>";
-      }
+      showSuccessMessage();
     }
     else
     {
       setEnabled(true);
-
-      progressLabel.setForeground(getDisplay().getSystemColor(SWT.COLOR_RED));
-      message = installError + "\n\n<a>" + TEXT_LOG + "</a>\n";
+      installButton.setCurrentState(State.INSTALL);
+      showErrorMessage();
     }
 
-    progressLabel.setText(message);
-    backButton.setEnabled(true);
-
-    cancelButton.setVisible(false);
-    installStack.setTopControl(installButton);
-
+    setEnabled(true);
     dialog.setButtonsEnabled(true);
   }
 
+  private void showSuccessMessage()
+  {
+    dialog.showMessage(MESSAGE_SUCCESS, Type.SUCCESS, true);
+
+    installStack.setTopControl(afterInstallComposite);
+    installStack.setVisible(true);
+    layout(true, true);
+  }
+
+  private void showErrorMessage()
+  {
+    Runnable action = null;
+    String errorMessage = installError;
+
+    if (isInstallLogAvailable())
+    {
+      action = new Runnable()
+      {
+        public void run()
+        {
+          openInstallLog();
+        }
+      };
+
+      errorMessage += " <a>Show log</a>.";
+    }
+
+    dialog.showMessage(errorMessage, Type.ERROR, false, action);
+
+    installStack.setTopControl(errorComposite);
+    installStack.setVisible(true);
+    layout(true, true);
+  }
+
   private void launchProduct()
   {
     try
@@ -991,7 +891,6 @@
   private void setFolderText(String dir)
   {
     folderText.setText(dir);
-    folderText.setSelection(dir.length());
   }
 
   private void validateFolderText(String dir)
@@ -1015,6 +914,17 @@
     }
   }
 
+  private void openInstallLog()
+  {
+    File installationLogFile = new File(installFolder, SETUP_LOG_FILE);
+    dialog.showInstallationLog(installationLogFile);
+  }
+
+  private boolean isInstallLogAvailable()
+  {
+    return new File(installFolder, SETUP_LOG_FILE).exists();
+  }
+
   /**
    * @author Eike Stepper
    */
@@ -1070,7 +980,7 @@
   /**
    * @author Eike Stepper
    */
-  private final class SimplePrompter extends HashMap<String, String>implements SetupPrompter
+  private final class SimplePrompter extends HashMap<String, String> implements SetupPrompter
   {
     private static final long serialVersionUID = 1L;
 
@@ -1128,8 +1038,6 @@
 
     private volatile boolean done;
 
-    private int lastSelection = -1;
-
     private String lastName;
 
     public void setTerminating()
@@ -1245,24 +1153,18 @@
 
       if (!canceled)
       {
-        int smin = progressBar.getMinimum();
-        int smax = progressBar.getMaximum();
-        int selection = (int)(work * (smax - smin) / totalWork + smin);
+        double progress = work / totalWork;
 
         try
         {
-          if (selection != lastSelection)
-          {
-            lastSelection = selection;
-            progressBar.setSelection(selection);
-          }
+          installButton.setProgress((float)progress);
 
           if (!ObjectUtil.equals(name, lastName))
           {
             lastName = name;
             if (!done)
             {
-              progressLabel.setText(StringUtil.safe(name));
+              installButton.setToolTipText(StringUtil.safe(name));
             }
           }
         }
diff --git a/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/FlatButton.java b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/FlatButton.java
new file mode 100644
index 0000000..571c7f2
--- /dev/null
+++ b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/FlatButton.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.internal.ui;
+
+import org.eclipse.oomph.ui.UIUtil;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Special button that does not draw any borders on hover/down coming from the OS.
+ *
+ * @author Andreas Scharf
+ */
+public class FlatButton extends Canvas implements Listener, PaintListener
+{
+  private static final int DEFAULT_ICON_TEXT_GAP = 5;
+
+  private static final Color COLOR_DEFAULT_DISABLED = UIPlugin.getColor(210, 210, 210);
+
+  private final List<SelectionListener> selectionListeners = new ArrayList<SelectionListener>();
+
+  private final int buttonStyle;
+
+  private boolean hover;
+
+  private Color hoverColor = UIUtil.COLOR_PURPLE;
+
+  private Color disabledColor = COLOR_DEFAULT_DISABLED;
+
+  private Color internalBackgroundColor;
+
+  private Image image;
+
+  private String text;
+
+  private int cornerWidth;
+
+  private int iconTextGap = DEFAULT_ICON_TEXT_GAP;
+
+  private int alignment = SWT.LEFT;
+
+  private boolean listenersPaused;
+
+  private boolean mouseDown;
+
+  private boolean mouseLockedInBounds;
+
+  private boolean showButtonDownState = true;
+
+  public FlatButton(Composite parent, int buttonStyle)
+  {
+    super(parent, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
+    this.buttonStyle = buttonStyle;
+
+    setLayout(new GridLayout(1, false));
+    setCursor(UIUtil.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+
+    hookListeners();
+  }
+
+  public void setText(String text)
+  {
+    if (this.text != text)
+    {
+      this.text = text;
+      redraw();
+    }
+  }
+
+  public void setImage(Image image)
+  {
+    if (this.image != image)
+    {
+      this.image = image;
+      redraw();
+    }
+  }
+
+  public Image getImage()
+  {
+    return image;
+  }
+
+  public void setAlignment(int alignment)
+  {
+    switch (alignment)
+    {
+      case SWT.LEFT:
+      case SWT.CENTER:
+      case SWT.RIGHT:
+        // Do nothing.
+        break;
+
+      default:
+        throw new IllegalArgumentException("Alignment must be one of SWT.LEFT, SWT.CENTER, SWT.RIGHT");
+    }
+
+    if (this.alignment != alignment)
+    {
+      this.alignment = alignment;
+      redraw();
+    }
+  }
+
+  public int getAlignment()
+  {
+    return alignment;
+  }
+
+  @Override
+  public Point computeSize(int wHint, int hHint, boolean changed)
+  {
+    Point size = null;
+
+    if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT)
+    {
+      size = new Point(wHint, hHint);
+    }
+    else
+    {
+      size = getTotalSize();
+    }
+
+    if (wHint != SWT.DEFAULT)
+    {
+      size.x = wHint;
+    }
+
+    if (hHint != SWT.DEFAULT)
+    {
+      size.y = hHint;
+    }
+
+    // Extend the size to be able to visualize button down state.
+    size.x += 1;
+    size.y += 1;
+
+    return size;
+  }
+
+  protected Point getTotalSize()
+  {
+    int width = 0;
+    int height = 0;
+    if (text != null)
+    {
+      GC gc = new GC(this);
+      Point textSize = gc.textExtent(text);
+      width += textSize.x;
+
+      if (image != null)
+      {
+        width += iconTextGap;
+      }
+
+      height += textSize.y;
+    }
+
+    if (image != null)
+    {
+      Rectangle imgBounds = image.getBounds();
+      width += imgBounds.width;
+
+      int heightDiff = imgBounds.height - height;
+      if (heightDiff > 0)
+      {
+        height += heightDiff;
+      }
+    }
+
+    return new Point(width, height);
+  }
+
+  protected void hookListeners()
+  {
+    addListener(SWT.MouseEnter, this);
+    addListener(SWT.MouseExit, this);
+    addListener(SWT.MouseDown, this);
+    addListener(SWT.MouseUp, this);
+    addListener(SWT.MouseMove, this);
+
+    addPaintListener(this);
+  }
+
+  protected void unhookListeners()
+  {
+    removePaintListener(this);
+
+    removeListener(SWT.MouseMove, (Listener)this);
+    removeListener(SWT.MouseUp, (Listener)this);
+    removeListener(SWT.MouseDown, (Listener)this);
+    removeListener(SWT.MouseExit, (Listener)this);
+    removeListener(SWT.MouseEnter, (Listener)this);
+  }
+
+  @Override
+  public void dispose()
+  {
+    unhookListeners();
+    super.dispose();
+  }
+
+  public void setIconTextGap(int iconTextGap)
+  {
+    if (this.iconTextGap != iconTextGap)
+    {
+      this.iconTextGap = iconTextGap;
+
+      Composite parent = getParent();
+      if (parent != null)
+      {
+        parent.layout();
+      }
+    }
+  }
+
+  /**
+   * Called when this button should paint itself.
+   *
+   * Subclasses may implement.
+   */
+  protected void drawContent(PaintEvent e)
+  {
+    GC gc = e.gc;
+
+    Point totalSize = getTotalSize();
+
+    Rectangle clientArea = getClientArea();
+
+    int startX = 0;
+
+    switch (getAlignment())
+    {
+      case SWT.CENTER:
+        startX = (clientArea.x + clientArea.width - totalSize.x) / 2;
+        break;
+
+      case SWT.RIGHT:
+        startX = clientArea.x + clientArea.width - totalSize.x;
+        break;
+    }
+
+    if (showButtonDownState && mouseLockedInBounds)
+    {
+      startX++;
+    }
+
+    if (image != null)
+    {
+      Rectangle imgBounds = image.getBounds();
+      int imgY = (clientArea.y + clientArea.height - imgBounds.height) / 2;
+
+      if (showButtonDownState && mouseLockedInBounds)
+      {
+        imgY++;
+      }
+
+      drawImage(gc, startX, imgY);
+
+      startX += imgBounds.width;
+      if (text != null)
+      {
+        startX += iconTextGap;
+      }
+    }
+
+    if (text != null)
+    {
+      Point textExtent = gc.textExtent(text);
+      int textY = (clientArea.y + clientArea.height - textExtent.y) / 2;
+
+      if (showButtonDownState && mouseLockedInBounds)
+      {
+        textY += 1;
+      }
+
+      drawText(gc, startX, textY);
+    }
+  }
+
+  protected void drawText(GC gc, int x, int y)
+  {
+    if (isEnabled())
+    {
+      if (isHover() && !listenersPaused && hoverColor != null)
+      {
+        gc.setForeground(hoverColor);
+      }
+    }
+    else if (disabledColor != null)
+    {
+      gc.setForeground(disabledColor);
+    }
+
+    gc.drawText(text, x, y, true);
+  }
+
+  protected void drawImage(GC gc, int x, int y)
+  {
+    gc.drawImage(image, x, y);
+  }
+
+  @Override
+  public int getStyle()
+  {
+    return buttonStyle;
+  }
+
+  @Override
+  protected void checkSubclass()
+  {
+    // Allow subclassing.
+  }
+
+  public boolean isHover()
+  {
+    return hover;
+  }
+
+  public void setShowButtonDownState(boolean showButtonDownState)
+  {
+    if (this.showButtonDownState != showButtonDownState)
+    {
+      this.showButtonDownState = showButtonDownState;
+      redraw();
+    }
+  }
+
+  public void handleEvent(Event event)
+  {
+    switch (event.type)
+    {
+      case SWT.MouseEnter:
+        onMouseEnter(event);
+        break;
+
+      case SWT.MouseUp:
+        onMouseUp(event);
+        break;
+
+      case SWT.MouseExit:
+        onMouseExit(event);
+        break;
+
+      case SWT.MouseDown:
+        onMouseDown(event);
+        break;
+
+      case SWT.MouseMove:
+        mouseMoveInternal(event);
+    }
+
+    if (!isDisposed())
+    {
+      redraw();
+    }
+  }
+
+  private void mouseMoveInternal(Event event)
+  {
+    if (mouseDown)
+    {
+      Rectangle bounds = getBounds();
+      boolean inBounds = event.x >= 0 && event.x <= bounds.width && event.y >= 0 && event.y <= bounds.height;
+      if (inBounds != mouseLockedInBounds)
+      {
+        mouseLockedInBounds = inBounds;
+        if (inBounds)
+        {
+          onMouseEnter(event);
+        }
+        else
+        {
+          onMouseExit(event);
+        }
+      }
+    }
+  }
+
+  protected void onMouseDown(Event event)
+  {
+    mouseDown = true;
+    mouseLockedInBounds = true;
+  }
+
+  protected void onMouseExit(Event event)
+  {
+    setHover(false);
+  }
+
+  protected void onMouseUp(Event event)
+  {
+    mouseLockedInBounds = false;
+    mouseDown = false;
+    setHover(true);
+    notifySelectionListeners(new SelectionEvent(event));
+  }
+
+  protected void onMouseEnter(Event event)
+  {
+    setHover(true);
+  }
+
+  private void setHover(boolean hover)
+  {
+    if (this.hover != hover)
+    {
+      this.hover = hover;
+      onHover();
+    }
+  }
+
+  /**
+   * Called when the hover status of this button changes.
+   */
+  protected void onHover()
+  {
+    // Subclasses may implement.
+  }
+
+  private void notifySelectionListeners(SelectionEvent event)
+  {
+    if (!isEnabled() || listenersPaused)
+    {
+      return;
+    }
+
+    Rectangle r = getBounds();
+    if (event.x >= 0 && event.x <= r.width && event.y >= 0 && event.y <= r.height && !selectionListeners.isEmpty())
+    {
+      for (SelectionListener listener : selectionListeners)
+      {
+        try
+        {
+          listener.widgetSelected(event);
+        }
+        catch (Exception ex)
+        {
+          UIPlugin.INSTANCE.log(ex);
+        }
+      }
+    }
+  }
+
+  protected boolean isListenersPaused()
+  {
+    return listenersPaused;
+  }
+
+  protected void setListenersPaused(boolean pause)
+  {
+    listenersPaused = pause;
+  }
+
+  public void setCornerWidth(int cornerWidth)
+  {
+    if (this.cornerWidth != cornerWidth)
+    {
+      this.cornerWidth = cornerWidth;
+      redraw();
+    }
+  }
+
+  public int getCornerWidth()
+  {
+    return cornerWidth;
+  }
+
+  public void addSelectionListener(SelectionListener listener)
+  {
+    selectionListeners.add(listener);
+  }
+
+  public void removeSelectionListener(SelectionListener listener)
+  {
+    selectionListeners.remove(selectionListeners);
+  }
+
+  public final void paintControl(PaintEvent e)
+  {
+    Rectangle clientBounds = getClientArea();
+    GC gc = e.gc;
+    gc.setAntialias(SWT.ON);
+    int clientX = clientBounds.x;
+    int clientY = clientBounds.y;
+
+    int clientWidth = clientBounds.width - 1;
+    int clientHeight = clientBounds.height - 1;
+
+    if (showButtonDownState && mouseLockedInBounds)
+    {
+      clientX++;
+      clientY++;
+    }
+
+    clearBackground(gc, clientBounds);
+
+    drawBackground(gc, clientX, clientY, clientWidth, clientHeight, 0, 0);
+    drawContent(e);
+    if (isHover())
+    {
+      drawHoverState(gc, clientX, clientY, clientWidth, clientHeight);
+    }
+  }
+
+  private void clearBackground(GC gc, Rectangle bounds)
+  {
+    Composite parent = getParent();
+    Color parentBackground = null;
+    do
+    {
+      parentBackground = parent.getBackground();
+      parent = parent.getParent();
+    } while (parentBackground == null && parent.getParent() != null);
+
+    if (parentBackground != null)
+    {
+      Color oldBG = gc.getBackground();
+      gc.setBackground(parentBackground);
+      gc.fillRectangle(bounds);
+      gc.setBackground(oldBG);
+    }
+  }
+
+  protected void drawHoverState(GC gc, int x, int y, int width, int height)
+  {
+    // Subclasses may implement to draw a hover state.
+  }
+
+  @Override
+  public void drawBackground(GC gc, int x, int y, int width, int height, int offsetX, int offsetY)
+  {
+    if (internalBackgroundColor != null)
+    {
+      gc.setBackground(internalBackgroundColor);
+      gc.fillRoundRectangle(x, y, width, height, cornerWidth, cornerWidth);
+    }
+  }
+
+  public Color getHoverColor()
+  {
+    return hoverColor;
+  }
+
+  public void setHoverColor(Color hoverColor)
+  {
+    if (this.hoverColor != hoverColor)
+    {
+      this.hoverColor = hoverColor;
+      redraw();
+    }
+  }
+
+  @Override
+  public void setBackground(Color color)
+  {
+    if (internalBackgroundColor != color)
+    {
+      internalBackgroundColor = color;
+      redraw();
+    }
+  }
+
+  @Override
+  public Color getBackground()
+  {
+    return internalBackgroundColor;
+  }
+
+  public Color getDisabledColor()
+  {
+    return disabledColor;
+  }
+
+  public void setDisabledColor(Color disabledColor)
+  {
+    if (this.disabledColor != disabledColor)
+    {
+      this.disabledColor = disabledColor;
+      redraw();
+    }
+  }
+}
diff --git a/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/ImageCheckbox.java b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/ImageCheckbox.java
new file mode 100644
index 0000000..90ca12c
--- /dev/null
+++ b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/ImageCheckbox.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.internal.ui;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @author Andreas Scharf
+ */
+public class ImageCheckbox extends ImageHoverButton
+{
+  private final Image checkedImage;
+
+  private boolean checked;
+
+  public ImageCheckbox(Composite parent, Image defaultImage, Image hoverImage)
+  {
+    this(parent, defaultImage, hoverImage, hoverImage);
+  }
+
+  public ImageCheckbox(Composite parent, Image defaultImage, Image hoverImage, Image checkedImage)
+  {
+    super(parent, SWT.CHECK, defaultImage, hoverImage);
+    this.checkedImage = checkedImage;
+  }
+
+  public boolean isChecked()
+  {
+    return checked;
+  }
+
+  public void setChecked(boolean checked)
+  {
+    this.checked = checked;
+    setImage(computeImage());
+    redraw();
+  }
+
+  @Override
+  protected Image computeImage()
+  {
+    if (!isEnabled())
+    {
+      return super.computeImage();
+    }
+
+    if (isHover() && !isChecked())
+    {
+      return getHoverImage();
+    }
+
+    return isChecked() ? getCheckedImage() : getDefaultImage();
+  }
+
+  protected Image getCheckedImage()
+  {
+    return checkedImage;
+  }
+}
diff --git a/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/ImageHoverButton.java b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/ImageHoverButton.java
new file mode 100644
index 0000000..c7fefea
--- /dev/null
+++ b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/ImageHoverButton.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015 The Eclipse Foundation 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:
+ *    Yatta Solutions - [466264] initial API and implementation
+ */
+package org.eclipse.oomph.internal.ui;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @author Andreas Scharf
+ */
+public class ImageHoverButton extends FlatButton
+{
+  private Image defaultImage;
+
+  private Image hoverImage;
+
+  private Image disabledImage;
+
+  public ImageHoverButton(Composite parent, int buttonStyle)
+  {
+    this(parent, buttonStyle, null, null);
+  }
+
+  public ImageHoverButton(Composite parent, int buttonStyle, Image image, Image hoverImage)
+  {
+    this(parent, buttonStyle, image, hoverImage, null);
+  }
+
+  public ImageHoverButton(Composite parent, int buttonStyle, Image image, Image hoverImage, Image disabledImage)
+  {
+    super(parent, buttonStyle);
+    defaultImage = image;
+    this.hoverImage = hoverImage;
+    this.disabledImage = disabledImage;
+
+    setImage(computeImage());
+  }
+
+  protected Image computeImage()
+  {
+    if (!isEnabled())
+    {
+      return getDisabledImage() != null ? getDisabledImage() : getDefaultImage();
+    }
+
+    return isHover() ? getHoverImage() : getDefaultImage();
+  }
+
+  @Override
+  public void setEnabled(boolean enabled)
+  {
+    super.setEnabled(enabled);
+    setImage(computeImage());
+  }
+
+  public void setDefaultImage(Image defaultImage)
+  {
+    if (this.defaultImage != defaultImage)
+    {
+      this.defaultImage = defaultImage;
+      setImage(computeImage());
+    }
+  }
+
+  public Image getDefaultImage()
+  {
+    return defaultImage;
+  }
+
+  public void setHoverImage(Image hoverImage)
+  {
+    if (this.hoverImage != hoverImage)
+    {
+      this.hoverImage = hoverImage;
+      setImage(computeImage());
+    }
+  }
+
+  public Image getHoverImage()
+  {
+    return hoverImage;
+  }
+
+  @Override
+  protected void onHover()
+  {
+    setImage(computeImage());
+  }
+
+  public Image getDisabledImage()
+  {
+    return disabledImage;
+  }
+
+  public void setDisabledImage(Image disabledImage)
+  {
+    if (this.disabledImage != disabledImage)
+    {
+      this.disabledImage = disabledImage;
+      setImage(computeImage());
+    }
+  }
+}
diff --git a/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/ui/UIUtil.java b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/ui/UIUtil.java
index dd37d39..95932d8 100644
--- a/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/ui/UIUtil.java
+++ b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/ui/UIUtil.java
@@ -22,6 +22,7 @@
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Resource;
@@ -45,6 +46,8 @@
 {
   public static final IWorkbench WORKBENCH;
 
+  public static final Color COLOR_PURPLE = UIPlugin.getColor(44, 34, 85);
+
   private static Image ERROR_IMAGE;
 
   private static Image WARNING_IMAGE;
diff --git a/plugins/org.eclipse.oomph.util/src/org/eclipse/oomph/util/StringUtil.java b/plugins/org.eclipse.oomph.util/src/org/eclipse/oomph/util/StringUtil.java
index dc8de5f..be524ef 100644
--- a/plugins/org.eclipse.oomph.util/src/org/eclipse/oomph/util/StringUtil.java
+++ b/plugins/org.eclipse.oomph.util/src/org/eclipse/oomph/util/StringUtil.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *    Eike Stepper - initial API and implementation
+ *    Yatta Solutions - [466264] Enhance UX in simple installer
  */
 package org.eclipse.oomph.util;
 
@@ -241,4 +242,103 @@
 
     return uri;
   }
+
+  /**
+   * Shortens the given text to be as long as the given length (including the
+   * appended ellipsis).
+   *
+   * @param input The text to shorten.
+   * @param length The maximum length of the resulting text, ellipsis included.
+   * @param wholeWord Whether to take care for splitting the text at word
+   * boundaries only.
+   */
+  public static String shorten(String input, int length, boolean wholeWord)
+  {
+    if (input == null)
+    {
+      throw new IllegalArgumentException("Input string must not be null");
+    }
+
+    if (input.length() <= length)
+    {
+      return input;
+    }
+
+    int ellipsisIdx = length - 4;
+
+    if (wholeWord)
+    {
+      ellipsisIdx = findLastSpaceBetween(input, 0, ellipsisIdx);
+    }
+
+    String result = input.substring(0, ellipsisIdx);
+    result += " ...";
+    return result;
+  }
+
+  public static String wrapText(String input, int maxCharactersPerLine, boolean wholeWord)
+  {
+    int startIndex = 0;
+    int endIndex = startIndex + maxCharactersPerLine;
+    boolean finished = false;
+
+    StringBuilder builder = new StringBuilder();
+
+    do
+    {
+      if (endIndex >= input.length())
+      {
+        endIndex = input.length();
+        finished = true;
+      }
+
+      if (!finished && wholeWord)
+      {
+        int spaceIndex = findLastSpaceBetween(input, startIndex, endIndex);
+        if (spaceIndex > 0)
+        {
+          endIndex = spaceIndex;
+        }
+        else
+        {
+          // No more spaces till end.
+          endIndex = input.length();
+          finished = true;
+        }
+      }
+
+      builder.append(input.substring(startIndex, endIndex));
+
+      if (!finished)
+      {
+        builder.append(StringUtil.NL);
+      }
+
+      startIndex = wholeWord ? endIndex + 1 : endIndex;
+      endIndex += maxCharactersPerLine;
+
+    } while (!finished);
+
+    return builder.toString();
+  }
+
+  private static int findLastSpaceBetween(String text, int startPosition, int endPosition)
+  {
+    int index = endPosition;
+    char lastBeforeEllipsis = text.charAt(index);
+
+    while (lastBeforeEllipsis != ' ')
+    {
+      index--;
+      if (index <= startPosition)
+      {
+        index = -1;
+        break;
+      }
+
+      lastBeforeEllipsis = text.charAt(index);
+    }
+
+    return index;
+  }
 }