[455829] Provide a simplified installer that supports switching between simple and advanced modes

https://bugs.eclipse.org/bugs/show_bug.cgi?id=455829
diff --git a/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/SetupWizardPage.java b/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/SetupWizardPage.java
index 97b3565..77312d8 100644
--- a/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/SetupWizardPage.java
+++ b/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/SetupWizardPage.java
@@ -16,6 +16,7 @@
 import org.eclipse.oomph.setup.Workspace;
 import org.eclipse.oomph.setup.internal.core.SetupTaskPerformer;
 import org.eclipse.oomph.setup.internal.core.util.CatalogManager;
+import org.eclipse.oomph.setup.ui.SetupUIPlugin;
 import org.eclipse.oomph.ui.HelpSupport.HelpProvider;
 import org.eclipse.oomph.ui.OomphWizardDialog;
 import org.eclipse.oomph.ui.PersistentButton;
@@ -27,19 +28,28 @@
 import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
 
 import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.wizard.IWizardContainer;
 import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardDialog;
 import org.eclipse.jface.wizard.WizardPage;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
 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.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @author Eike Stepper
@@ -154,6 +164,27 @@
     super.setControl(pageControl);
     setPageComplete(false);
 
+    if (false)
+    {
+      StepIndicatorCanvas canvas = new StepIndicatorCanvas(pageControl, SWT.NONE);
+      canvas.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+
+      List<String> steps = new ArrayList<String>();
+      IWizardPage[] pages = getWizard().getPages();
+      for (int i = 0; i < pages.length; i++)
+      {
+        IWizardPage page = pages[i];
+        steps.add(page.getTitle());
+
+        if (page == this)
+        {
+          canvas.setCurrentStep(i);
+        }
+      }
+
+      canvas.setSteps(steps.toArray(new String[steps.size()]));
+    }
+
     Composite uiContainer = new Composite(pageControl, SWT.NONE);
     uiContainer.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
     uiContainer.setLayout(createGridLayout(1));
@@ -276,6 +307,74 @@
     }
   }
 
+  private TitleBarUpdater titleBarUpdater = new StepIndicatorTitleBarUpdater();
+
+  @Override
+  public void setImageDescriptor(ImageDescriptor imageDescriptor)
+  {
+    titleBarUpdater.setImageDescriptor(imageDescriptor);
+  }
+
+  @Override
+  public String getTitle()
+  {
+    return titleBarUpdater.getTitle();
+  }
+
+  @Override
+  public void setTitle(String title)
+  {
+    titleBarUpdater.setTitle(title);
+  }
+
+  @Override
+  public void setDescription(String description)
+  {
+    titleBarUpdater.setDescription(description);
+  }
+
+  @Override
+  public void setMessage(String message, int type)
+  {
+    titleBarUpdater.setMessage(message, type);
+  }
+
+  @Override
+  public void setErrorMessage(String message)
+  {
+    titleBarUpdater.setErrorMessage(message);
+  }
+
+  private void superSetImageDescriptor(ImageDescriptor imageDescriptor)
+  {
+    super.setImageDescriptor(imageDescriptor);
+  }
+
+  public String superGetTitle()
+  {
+    return super.getTitle();
+  }
+
+  private void superSetTitle(String title)
+  {
+    super.setTitle(title);
+  }
+
+  private void superSetDescription(String description)
+  {
+    super.setDescription(description);
+  }
+
+  private void superSetMessage(String message, int type)
+  {
+    super.setMessage(message, type);
+  }
+
+  private void superSetErrorMessage(String message)
+  {
+    super.setErrorMessage(message);
+  }
+
   protected static GridLayout createGridLayout(int numColumns)
   {
     GridLayout layout = new GridLayout(numColumns, false);
@@ -283,4 +382,198 @@
     layout.marginHeight = 0;
     return layout;
   }
+
+  /**
+   * @author Eike Stepper
+   */
+  public interface TitleBarUpdater
+  {
+    public void setImageDescriptor(ImageDescriptor imageDescriptor);
+
+    public String getTitle();
+
+    public void setTitle(String title);
+
+    public void setDescription(String description);
+
+    public void setMessage(String message, int type);
+
+    public void setErrorMessage(String message);
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class DefaultTitleBarUpdater implements TitleBarUpdater
+  {
+    public void setImageDescriptor(ImageDescriptor imageDescriptor)
+    {
+      superSetImageDescriptor(imageDescriptor);
+    }
+
+    public String getTitle()
+    {
+      return superGetTitle();
+    }
+
+    public void setTitle(String title)
+    {
+      superSetTitle(title);
+    }
+
+    public void setDescription(String description)
+    {
+      superSetDescription(description);
+    }
+
+    public void setMessage(String message, int type)
+    {
+      superSetMessage(message, type);
+    }
+
+    public void setErrorMessage(String message)
+    {
+      superSetErrorMessage(message);
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class StepIndicatorTitleBarUpdater implements TitleBarUpdater
+  {
+    private static final int BORDER_WIDTH = 10;
+
+    private final Image titleImage = SetupUIPlugin.INSTANCE.getSWTImage("install_wiz.png");
+
+    private final ImageData titleImageData = titleImage.getImageData();
+
+    private ImageDescriptor imageDescriptor;
+
+    private String title;
+
+    private String description;
+
+    private String message;
+
+    private int type;
+
+    private String errorMessage;
+
+    private boolean layoutChanged;
+
+    public StepIndicatorTitleBarUpdater()
+    {
+
+    }
+
+    public void setImageDescriptor(ImageDescriptor imageDescriptor)
+    {
+      this.imageDescriptor = imageDescriptor;
+      updateTitleBar();
+    }
+
+    public String getTitle()
+    {
+      return title;
+    }
+
+    public void setTitle(String title)
+    {
+      this.title = title;
+      updateTitleBar();
+    }
+
+    public void setDescription(String description)
+    {
+      this.description = description;
+      updateTitleBar();
+    }
+
+    public void setMessage(String message, int type)
+    {
+      this.message = message;
+      this.type = type;
+      updateTitleBar();
+    }
+
+    public void setErrorMessage(String message)
+    {
+      errorMessage = message;
+      updateTitleBar();
+    }
+
+    private void updateTitleBar()
+    {
+      WizardDialog dialog = (WizardDialog)getContainer();
+      if (dialog == null)
+      {
+        return;
+      }
+
+      final Shell shell = dialog.getShell();
+
+      StepIndicator stepIndicator = new StepIndicator(shell.getDisplay(), getControl().getFont())
+      {
+        @Override
+        protected Rectangle getClientArea()
+        {
+          Rectangle clientArea = shell.getClientArea();
+          clientArea.width -= titleImageData.width + 2 * BORDER_WIDTH;
+          return clientArea;
+        }
+      };
+
+      List<String> steps = new ArrayList<String>();
+      IWizardPage[] pages = getWizard().getPages();
+      boolean currentPage = isCurrentPage();
+
+      for (int i = 0; i < pages.length; i++)
+      {
+        IWizardPage page = pages[i];
+        steps.add(page.getTitle());
+
+        if (currentPage)
+        {
+          stepIndicator.setCurrentStep(i);
+        }
+      }
+
+      stepIndicator.setSteps(steps.toArray(new String[steps.size()]));
+
+      if (currentPage)
+      {
+
+        int width = shell.getClientArea().width;
+        int height = titleImageData.height;
+
+        final Image buffer = new Image(shell.getDisplay(), width, height);
+
+        GC gc = new GC(buffer);
+        gc.drawImage(titleImage, width - titleImageData.width, 0);
+        stepIndicator.paint(gc, BORDER_WIDTH, 6);
+        gc.dispose();
+
+        ImageDescriptor descriptor = new ImageDescriptor()
+        {
+          @Override
+          public ImageData getImageData()
+          {
+            return buffer.getImageData();
+          }
+        };
+
+        superSetImageDescriptor(descriptor);
+
+        if (!layoutChanged)
+        {
+          shell.layout(true, true);
+          layoutChanged = true;
+        }
+
+        // dialog.setTitleImage(buffer);
+        // dialog.updateTitleBar();
+      }
+    }
+  }
 }
diff --git a/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/StepIndicator.java b/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/StepIndicator.java
new file mode 100644
index 0000000..eab6f0a
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/StepIndicator.java
@@ -0,0 +1,210 @@
+/*
+ * 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
+ */
+package org.eclipse.oomph.setup.ui.wizards;
+
+import org.eclipse.oomph.setup.ui.SetupUIPlugin;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @author Eike Stepper
+ */
+public abstract class StepIndicator
+{
+  private static final int BORDER = 3;
+
+  private final Display display;
+
+  private final Font normalFont;
+
+  private final Font boldFont;
+
+  private final Color whiteColor;
+
+  private final Color grayColor;
+
+  private final Color activeColor;
+
+  private String[] steps;
+
+  private int[] stepWidths;
+
+  private int[] stepWidthsBold;
+
+  private int extraWidth;
+
+  private int currentStep;
+
+  private int height = -1;
+
+  private int clientAreaWidth;
+
+  public StepIndicator(Display display, Font normalFont)
+  {
+    this.display = display;
+    this.normalFont = normalFont;
+    boldFont = SetupUIPlugin.getBoldFont(normalFont);
+
+    whiteColor = display.getSystemColor(SWT.COLOR_WHITE);
+    grayColor = display.getSystemColor(SWT.COLOR_DARK_GRAY);
+    activeColor = new Color(display, 222, 230, 248);
+  }
+
+  public void dispose()
+  {
+    activeColor.dispose();
+  }
+
+  public String[] getSteps()
+  {
+    return steps;
+  }
+
+  public void setSteps(String... steps)
+  {
+    this.steps = steps;
+  }
+
+  public int getCurrentStep()
+  {
+    return currentStep;
+  }
+
+  public void setCurrentStep(int currentStep)
+  {
+    this.currentStep = currentStep;
+  }
+
+  public void reset()
+  {
+    stepWidths = null;
+  }
+
+  public int getHeight(GC gc)
+  {
+    boolean newGC = false;
+    if (gc == null)
+    {
+      gc = new GC(display);
+      newGC = true;
+    }
+
+    try
+    {
+      gc.setFont(boldFont);
+      height = gc.stringExtent("Ag").y + 2 * BORDER;
+      if (height % 2 == 0)
+      {
+        ++height;
+      }
+
+      return height + BORDER;
+    }
+    finally
+    {
+      if (newGC)
+      {
+        gc.dispose();
+      }
+    }
+  }
+
+  public void paint(GC gc, int x, int y)
+  {
+    gc.setAdvanced(true);
+    gc.setAntialias(SWT.ON);
+
+    if (height == -1)
+    {
+      getHeight(gc);
+    }
+
+    int stepCount = steps.length;
+    if (stepWidths == null)
+    {
+      stepWidths = new int[stepCount];
+      stepWidthsBold = new int[stepCount];
+      int totalWidth = 0;
+
+      for (int i = 0; i < stepCount; i++)
+      {
+        String step = steps[i];
+
+        gc.setFont(boldFont);
+        Point extent = gc.stringExtent(step);
+        stepWidthsBold[i] = extent.x;
+
+        gc.setFont(normalFont);
+        extent = gc.stringExtent(step);
+        stepWidths[i] = extent.x;
+
+        totalWidth += stepWidthsBold[i];
+        if (i != 0)
+        {
+          totalWidth += BORDER;
+        }
+      }
+
+      Rectangle clientArea = getClientArea();
+      clientAreaWidth = clientArea.width;
+      extraWidth = Math.max(0, (clientAreaWidth - totalWidth) / stepCount);
+    }
+
+    Color oldBackground = gc.getBackground();
+    Color oldForeground = gc.getForeground();
+
+    for (int i = 0; i < stepCount; ++i)
+    {
+      String step = steps[i];
+      int width = stepWidthsBold[i] + extraWidth;
+
+      int x2 = i < stepCount - 1 ? x + width : clientAreaWidth - 1;
+      int[] polygon = { x, y, //
+          x2 - (i < stepCount + 1 ? 2 * BORDER : 0), y, //
+          x2, y + height / 2,//
+          x2 - (i < stepCount + 1 ? 2 * BORDER : 0), y + height - 1, //
+          x, y + height - 1,//
+          x + (i > 0 ? 2 * BORDER : 0), y + height / 2 };
+
+      int stringWidth;
+      if (i == currentStep)
+      {
+        gc.setBackground(activeColor);
+        gc.setFont(boldFont);
+        stringWidth = stepWidthsBold[i];
+      }
+      else
+      {
+        gc.setBackground(whiteColor);
+        gc.setFont(normalFont);
+        stringWidth = stepWidths[i];
+      }
+
+      gc.fillPolygon(polygon);
+      gc.setBackground(oldBackground);
+
+      gc.setForeground(grayColor);
+      gc.drawPolygon(polygon);
+      gc.setForeground(oldForeground);
+
+      gc.drawString(step, x + width / 2 - stringWidth / 2, y + BORDER, true);
+      x += width + BORDER;
+    }
+  }
+
+  protected abstract Rectangle getClientArea();
+}
diff --git a/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/StepIndicatorCanvas.java b/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/StepIndicatorCanvas.java
new file mode 100644
index 0000000..63428b1
--- /dev/null
+++ b/plugins/org.eclipse.oomph.setup.ui/src/org/eclipse/oomph/setup/ui/wizards/StepIndicatorCanvas.java
@@ -0,0 +1,94 @@
+/*
+ * 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
+ */
+package org.eclipse.oomph.setup.ui.wizards;
+
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @author Eike Stepper
+ */
+public class StepIndicatorCanvas extends Canvas implements ControlListener, PaintListener
+{
+  private final StepIndicator stepIndicator;
+
+  public StepIndicatorCanvas(Composite parent, int style)
+  {
+    super(parent, style);
+    addControlListener(this);
+    addPaintListener(this);
+
+    stepIndicator = new StepIndicator(parent.getDisplay(), parent.getFont())
+    {
+      @Override
+      protected Rectangle getClientArea()
+      {
+        return StepIndicatorCanvas.this.getClientArea();
+      }
+    };
+  }
+
+  @Override
+  public void dispose()
+  {
+    stepIndicator.dispose();
+    super.dispose();
+  }
+
+  public String[] getSteps()
+  {
+    return stepIndicator.getSteps();
+  }
+
+  public void setSteps(String... steps)
+  {
+    stepIndicator.setSteps(steps);
+  }
+
+  public int getCurrentStep()
+  {
+    return stepIndicator.getCurrentStep();
+  }
+
+  public void setCurrentStep(int currentStep)
+  {
+    stepIndicator.setCurrentStep(currentStep);
+  }
+
+  public void controlMoved(ControlEvent e)
+  {
+    // Do nothing.
+  }
+
+  public void controlResized(ControlEvent e)
+  {
+    stepIndicator.reset();
+  }
+
+  @Override
+  public Point computeSize(int wHint, int hHint, boolean changed)
+  {
+    Point initialSize = super.computeSize(wHint, hHint, changed);
+    initialSize.y = stepIndicator.getHeight(null);
+    return initialSize;
+  }
+
+  public void paintControl(PaintEvent e)
+  {
+    stepIndicator.paint(e.gc, 0, 0);
+  }
+}