diff options
author | Andreas Scharf | 2015-05-06 09:01:47 +0000 |
---|---|---|
committer | Eike Stepper | 2015-05-07 09:43:49 +0000 |
commit | 90448e85d1b37c9ee475e356b2dc378450dc9bda (patch) | |
tree | 24dd1409a6fdfeb2c3f9f14b2b65dbfd338bafa7 | |
parent | b8dee398397070e14a3651a38eae713763cf449a (diff) | |
download | org.eclipse.oomph-committers/estepper/new-simple-ui-2.tar.gz org.eclipse.oomph-committers/estepper/new-simple-ui-2.tar.xz org.eclipse.oomph-committers/estepper/new-simple-ui-2.zip |
[466264] Enhance UX in simple installercommitters/estepper/new-simple-ui-2
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>
63 files changed, 3690 insertions, 889 deletions
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 e533d5023..38e5abbd7 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.SelectionChangedEvent; 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 @@ public abstract class JREController implements ISelectionChangedListener private boolean refreshing; + private Color viewerForegroundColor; + public JREController(Label label, StructuredViewer viewer, Request.Handler downloadHandler) { this.label = label; @@ -69,6 +73,7 @@ public abstract class JREController implements ISelectionChangedListener if (viewer != null) { viewer.addSelectionChangedListener(this); + viewerForegroundColor = viewer.getControl().getForeground(); } } @@ -269,6 +274,7 @@ public abstract class JREController implements ISelectionChangedListener protected void setLabel(String text) { label.setText(text); + label.getParent().layout(); } protected JREFilter createJREFilter() @@ -290,7 +296,7 @@ public abstract class JREController implements ISelectionChangedListener } 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 f3ff9d5bb..6847259de 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 class AgentManagerDialog extends OomphDialog { 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 @@ public class AgentManagerDialog extends OomphDialog @Override protected String getDefaultMessage() { - return MESSAGE; + return MESSAGE + "."; } @Override @@ -98,7 +98,7 @@ public class AgentManagerDialog extends OomphDialog 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 02c63110c..64b3c79ee 100644 --- a/plugins/org.eclipse.oomph.setup.installer/build.properties +++ b/plugins/org.eclipse.oomph.setup.installer/build.properties @@ -19,7 +19,9 @@ bin.includes = plugin.xml,\ 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 Binary files differnew file mode 100644 index 000000000..db433349b --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/fonts/OpenSans-Regular.ttf 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 000000000..af943ff21 --- /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 000000000..b093ae693 --- /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 000000000..e06e754d6 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/html/css/simpleInstaller.css @@ -0,0 +1,106 @@ +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%; + transform: translate(-50%, -50%); + left: 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 Binary files differnew file mode 100644 index 000000000..9881781b0 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit.png 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 Binary files differnew file mode 100644 index 000000000..a78eb4e33 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/32bit_hover.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit.png Binary files differnew file mode 100644 index 000000000..0621339a2 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit.png 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 Binary files differnew file mode 100644 index 000000000..499a9af52 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/64bit_hover.png 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 Binary files differnew file mode 100644 index 000000000..8da050ec8 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left.png 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 Binary files differnew file mode 100644 index 000000000..3dd72bcf0 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_disabled.png 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 Binary files differnew file mode 100644 index 000000000..941bdd184 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/arrow_left_hover.png 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 Binary files differnew file mode 100644 index 000000000..4fe63985e --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/checkmark_checked.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/close.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close.png Binary files differnew file mode 100644 index 000000000..db9189f1c --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close.png 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 Binary files differnew file mode 100644 index 000000000..0107aef47 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_hover.png 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 Binary files differnew file mode 100644 index 000000000..3504a2023 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message.png 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 Binary files differnew file mode 100644 index 000000000..341e07341 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/close_message_hover.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/delete.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/delete.png Binary files differnew file mode 100644 index 000000000..2074e4936 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/delete.png 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 Binary files differnew file mode 100644 index 000000000..d752e58ee --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle.png 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 Binary files differnew file mode 100644 index 000000000..5e4e5ceed --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exclamation_circle_hover.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png Binary files differindex fddbc2bce..5e1a4f17a 100644 --- a/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit.png 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 Binary files differnew file mode 100644 index 000000000..5a867b3d1 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/exit_hover.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png Binary files differindex 472484f11..bf6ed9e31 100644 --- a/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder.png 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 Binary files differnew file mode 100644 index 000000000..e239ba268 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_disabled.png 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 Binary files differnew file mode 100644 index 000000000..2324d8859 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/folder_hover.png 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 Binary files differnew file mode 100644 index 000000000..7fc303642 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install.png 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 Binary files differnew file mode 100644 index 000000000..2b571bd0d --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_install_hover.png 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 Binary files differnew file mode 100644 index 000000000..74d41f4dd --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch.png 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 Binary files differnew file mode 100644 index 000000000..abb74d421 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/install_button_launch_hover.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu.png Binary files differnew file mode 100644 index 000000000..0cc202d14 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu.png 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 Binary files differnew file mode 100644 index 000000000..59c3bb3d1 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_disabled.png 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 Binary files differnew file mode 100644 index 000000000..a3dd77f8a --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/menu_hover.png 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 Binary files differnew file mode 100644 index 000000000..2f305a519 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/notification_overlay.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/search.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/search.png Binary files differnew file mode 100644 index 000000000..50addfa68 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/search.png diff --git a/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png b/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png Binary files differindex a52e95e7f..ad208e1e0 100644 --- a/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png +++ b/plugins/org.eclipse.oomph.setup.installer/icons/simple/title.png 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 0e1b6cefe..c48fc8251 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.setup.ui.AbstractSetupDialog; 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 @@ import org.eclipse.swt.widgets.Shell; */ 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 @@ public abstract class AbstractSimpleDialog extends Shell 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 @@ public abstract class AbstractSimpleDialog extends Shell } }); - 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 000000000..b2871d263 --- /dev/null +++ b/plugins/org.eclipse.oomph.setup.installer/src/org/eclipse/oomph/setup/internal/installer/InstallLaunchButton.java @@ -0,0 +1,172 @@ +/* + * 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); + break; + + default: + setCursor(UIUtil.getDisplay().getSystemCursor(SWT.CURSOR_HAND)); + setListenersPaused(false); + } + } + + 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 f503b0aae..5fc5bc277 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 @@ public class InstallerApplication implements IApplication { if (installerDialog[0] != null) { - ProxyPreferenceDialog proxyPreferenceDialog = new ProxyPreferenceDialog(installerDialog[0].getShell()); + NetworkConnectionsDialog proxyPreferenceDialog = new NetworkConnectionsDialog(installerDialog[0].getShell()); proxyPreferenceDialog.open(); } } @@ -189,7 +190,7 @@ public class InstallerApplication implements IApplication 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 5428ccdcc..d431e2503 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 @@ public final class InstallerDialog extends SetupWizardDialog implements Installe return; case 1: - new ProxyPreferenceDialog(getShell()).open(); + new NetworkConnectionsDialog(getShell()).open(); installer.reloadIndex(); shell.getDisplay().asyncExec(checkIndex); return; @@ -180,7 +180,7 @@ public final class InstallerDialog extends SetupWizardDialog implements Installe @Override public void widgetSelected(SelectionEvent e) { - Dialog dialog = new ProxyPreferenceDialog(getShell()); + Dialog dialog = new NetworkConnectionsDialog(getShell()); dialog.open(); } }); @@ -192,7 +192,7 @@ public final class InstallerDialog extends SetupWizardDialog implements Installe @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 000000000..7ecb50b4e --- /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 a52eaea6f..53b8de2b6 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.Shell; import org.eclipse.swt.widgets.Text; import java.io.File; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; /** @@ -45,12 +42,6 @@ import java.lang.reflect.InvocationTargetException; */ 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 @@ public final class KeepInstallerDialog extends AbstractSetupDialog } }); - if (getPowerShell() != null) + if (InstallerUtil.getPowerShell() != null) { new Label(parent, SWT.NONE); startMenuButton = new Button(parent, SWT.CHECK); @@ -234,7 +225,7 @@ public final class KeepInstallerDialog extends AbstractSetupDialog 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 @@ public final class KeepInstallerDialog extends AbstractSetupDialog 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 000000000..7d6681078 --- /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 index 175bbffc9..77f444e6e 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 @@ import java.lang.reflect.Method; * @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 index 8924fe4cd..547ea86c7 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 @@ import org.eclipse.swt.widgets.Shell; * @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 acc31bb75..237ac3664 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 final class SetupInstallerPlugin extends OomphUIPlugin { 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 final class SetupInstallerPlugin extends OomphUIPlugin 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 000000000..4a71b4a02 --- /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 000000000..9e41e0c80 --- /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 ebc113421..80dc5edc0 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 @@ public final class SimpleInstallerDialog extends AbstractSimpleDialog implements 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() + { + @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); + } + + 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) { - captureDownloadButton(); + installerMenu = createInstallerMenu(); } - 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() + 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 @@ public final class SimpleInstallerDialog extends AbstractSimpleDialog implements } }); - 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,60 +324,98 @@ public final class SimpleInstallerDialog extends AbstractSimpleDialog implements } }); - 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) { - exitSelected(); + showAbout(); } }); - 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, SWT.NONE, this); - variablePage = new SimpleVariablePage(stack, SWT.NONE, this); - - stackLayout.topControl = productPage; - productPage.setFocus(); - - Display display = getDisplay(); - - Thread updateSearcher = new UpdateSearcher(display); - updateSearcher.start(); - - display.timerExec(500, new Runnable() + SimpleInstallerMenu.InstallerMenuItem exitItem = new SimpleInstallerMenu.InstallerMenuItem(menu); + exitItem.setText("EXIT"); + exitItem.setDividerVisible(false); + exitItem.addSelectionListener(new SelectionAdapter() { - public void run() + @Override + public void widgetSelected(SelectionEvent e) { - installer.getResourceSet().getLoadOptions().put(ECFURIHandlerImpl.OPTION_CACHE_HANDLING, ECFURIHandlerImpl.CacheHandling.CACHE_WITHOUT_ETAG_CHECKING); - installer.loadIndex(); + exitSelected(); } }); + + return menu; } - public Installer getInstaller() + private void updateAvailable(boolean available) { - return installer; + menuButton.setNotificationVisible(available); + updateInstallerItem.setVisible(available); + installerMenu.layout(); } - public void setButtonsEnabled(boolean enabled) + private void manageBundlePools() { - if (updateButton != null) + final boolean[] enabled = { poolEnabled }; + + AgentManagerDialog dialog = new AgentManagerDialog(getShell()) { - updateButton.setEnabled(enabled); + @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 (advancedButton != null) + if (dialog.open() == AgentManagerDialog.OK) { - advancedButton.setEnabled(enabled); + enablePool(enabled[0]); + pool = (BundlePool)dialog.getSelectedElement(); } } @@ -203,77 +441,210 @@ public final class SimpleInstallerDialog extends AbstractSimpleDialog implements public void productSelected(Product product) { variablePage.setProduct(product); + switchToPage(variablePage); + } + + public void backSelected() + { + if (pageStack.size() <= 1) + { + return; + } + UIUtil.asyncExec(new Runnable() { public void run() { - variablePage.setFocus(); + SimpleInstallerPage currentPage = pageStack.pop(); + + try + { + currentPage.aboutToHide(); + } + catch (Exception ex) + { + SetupInstallerPlugin.INSTANCE.log(ex); + } + + SimpleInstallerPage previousPage = pageStack.peek(); + + stackLayout.topControl = previousPage; + stack.layout(); + + previousPage.aboutToShow(); + previousPage.setFocus(); } }); + } - stackLayout.topControl = variablePage; - stack.layout(); + public void showMessage(String message, MessageOverlay.Type type, boolean dismissAutomatically) + { + showMessage(message, type, dismissAutomatically, null); } - public void backSelected() + public void showMessage(String message, MessageOverlay.Type type, boolean dismissAutomatically, Runnable action) { - UIUtil.asyncExec(new Runnable() + clearMessage(); + + currentMessage = new MessageOverlay(this, type, new ControlRelocator() { - public void run() + public void relocate(Control control) { - productPage.reset(); // TODO Use JavaScript, so that the browser doesn't scroll to top! - productPage.setFocus(); + 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(); } - }); - stackLayout.topControl = productPage; - stack.layout(); + currentMessage = null; + } } - private void captureDownloadButton() + public void showReadme(URI readmeURI) { - final Shell captureShell = new Shell(this, SWT.NO_TRIM | SWT.MODELESS); - captureShell.setLayout(new FillLayout()); + readmePage.setReadmeURI(readmeURI); + switchToPage(readmePage); + } - Image image = SetupInstallerPlugin.INSTANCE.getSWTImage("/download.png"); + public void showInstallationLog(File installationLogFile) + { + installationLogPage.setInstallationLogFile(installationLogFile); + switchToPage(installationLogPage); + } - final ToolButton downloadActiveButton = new ToolButton(captureShell, SWT.RADIO, image, false); - downloadActiveButton.setBackground(WHITE); - downloadActiveButton.setSelection(true); + public void showKeepInstaller() + { + switchToPage(keepInstallerPage); + } - final ToolButton downloadHoverButton = new ToolButton(captureShell, SWT.PUSH, image, false); - downloadHoverButton.setBackground(WHITE); - downloadHoverButton.addMouseMoveListener(new MouseMoveListener() + private void switchToPage(final SimpleInstallerPage page) + { + if (page != null) { - public void mouseMove(MouseEvent e) + final SimpleInstallerPage currentPage = !pageStack.isEmpty() ? pageStack.peek() : null; + if (currentPage == null || currentPage != page) { - try - { - AccessUtil.save(new File("/develop/download_hover.png"), downloadHoverButton); - AccessUtil.save(new File("/develop/download_active.png"), downloadActiveButton); - } - catch (Exception ex) - { - ex.printStackTrace(); - } - finally + pageStack.push(page); + + UIUtil.asyncExec(new Runnable() { - // captureShell.dispose(); - } + 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); + } + } - captureShell.pack(); - captureShell.open(); + return css; + } - Point pt = getDisplay().map(downloadHoverButton, null, 10, 10); - downloadHoverButton.setFocus(); + static String getPageTemplate() + { + if (pageTemplate == null) + { + try + { + pageTemplate = readBundleResource("html/PageTemplate.html"); - Event event = new Event(); - event.type = SWT.MouseMove; - event.x = pt.x; - event.y = pt.y; - getDisplay().post(event); + // 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 final class SimpleInstallerDialog extends AbstractSimpleDialog implements { 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 000000000..76228ad54 --- /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 000000000..bca928322 --- /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 17c809ad0..b286d5d90 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,162 +42,184 @@ public abstract class SimpleInstallerPage extends Composite 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 - protected void checkSubclass() + public void setEnabled(boolean enabled) { - // Disable the check that prevents subclassing of SWT components. + if (backButton != null) + { + backButton.setEnabled(enabled); + } } - public static String hex(RGB color) + protected Text createTextField(Composite parent) { - return hex(color.red) + hex(color.green) + hex(color.blue); - } + Composite textContainer = createInputFieldWrapper(parent, 0, 7, 0, 7); + applyComboOrTextStyle(textContainer); - public static String hex(int byteValue) - { - String hexString = Integer.toHexString(byteValue); - if (hexString.length() == 1) - { - hexString = "0" + hexString; - } + 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 hexString; + return textField; } - /** - * @author Eike Stepper - */ - public static abstract class ImageButton extends Label implements MouseTrackListener, MouseMoveListener, MouseListener, SelectionListener + protected CCombo createComboBox(Composite parent, int style) { - private static final Color HOVER_COLOR = SetupInstallerPlugin.getColor(HOVER_RGB); - - private static final Color ACTIVE_COLOR = SetupInstallerPlugin.getColor(ACTIVE_RGB); + Composite comboContainer = createInputFieldWrapper(parent, 0, 0, 0, 7); + applyComboOrTextStyle(comboContainer); - private Color oldBackground; + CCombo combo = new CCombo(comboContainer, style); + combo.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, true).create()); + applyComboOrTextStyle(combo); - private boolean mouseDown; + return combo; + } - public ImageButton(Composite parent, Image image) - { - super(parent, SWT.NONE); - setImage(image); + 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; + } - addMouseTrackListener(this); - addMouseMoveListener(this); - addMouseListener(this); - } + protected abstract void createContent(Composite container); - public void mouseEnter(MouseEvent e) - { - if (oldBackground == null) - { - oldBackground = getBackground(); - } + public void aboutToShow() + { + // Subclasses may implement. + } - if (mouseDown) - { - setBackground(ACTIVE_COLOR); - } - else - { - setBackground(HOVER_COLOR); - } - } + public void aboutToHide() + { + // Subclasses may implement. + } - public void mouseExit(MouseEvent e) - { - if (oldBackground != null) - { - setBackground(oldBackground); - oldBackground = null; - } - } + 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); + } - public void mouseHover(MouseEvent e) - { - // Do nothing. - } + 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; + } - public void mouseMove(MouseEvent e) - { - Rectangle bounds = getBounds(); - bounds.x = 0; - bounds.y = 0; + @Override + protected void checkSubclass() + { + // Allow subclassing. + } - if (bounds.contains(e.x, e.y)) - { - if (oldBackground == null) - { - mouseEnter(null); - } - } - else - { - if (oldBackground != null) - { - mouseExit(null); - } - } - } + public static String hex(RGB color) + { + return hex(color.red) + hex(color.green) + hex(color.blue); + } - public void mouseDoubleClick(MouseEvent e) + public static String hex(int byteValue) + { + String hexString = Integer.toHexString(byteValue); + if (hexString.length() == 1) { - // Do nothing. + hexString = "0" + hexString; } - public void mouseDown(MouseEvent e) - { - mouseDown = true; - setBackground(ACTIVE_COLOR); - } + return hexString; + } - public void mouseUp(MouseEvent e) - { - if (oldBackground != null) - { - setBackground(HOVER_COLOR); - widgetSelected(); - } - else - { - mouseExit(null); - } + /** + * @author Andreas Scharf + */ + private static class BackButton extends ImageHoverButton + { + private static final Image ARROW_LEFT = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/arrow_left.png"); - mouseDown = false; - } + private static final Image ARROW_LEFT_HOVER = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/arrow_left_hover.png"); - public void widgetDefaultSelected(SelectionEvent e) - { - // Do nothing. - } + private static final Image ARROW_LEFT_DISABLED = SetupInstallerPlugin.INSTANCE.getSWTImage("simple/arrow_left_disabled.png"); - public void widgetSelected(SelectionEvent e) - { - widgetSelected(); - } + private static final Font FONT = SetupInstallerPlugin.getFont(SimpleInstallerDialog.getDefaultFont(), URI.createURI("font:///10/bold")); - @Override - protected void checkSubclass() + public BackButton(Composite parent) { - // Disable the check that prevents subclassing of SWT components. + super(parent, SWT.PUSH, ARROW_LEFT, ARROW_LEFT_HOVER, ARROW_LEFT_DISABLED); + setIconTextGap(16); + setText("BACK"); + setForeground(SetupInstallerPlugin.COLOR_LABEL_FOREGROUND); + setFont(FONT); } - - 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 000000000..a78383ea3 --- /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 d086d57e0..05d82f852 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.internal.core.util.CatalogManager; 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.emf.ecore.resource.ResourceSet; 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 @@ import java.util.List; */ public class SimpleProductPage extends SimpleInstallerPage implements FilterHandler { - 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 int MAX_DESCRIPTION_LENGTH = 120; - private static final boolean FANCY = false; // OS.INSTANCE.isWin(); + private static final String PRODUCT_PREFIX = "product://"; - private SearchField searchField; + private SimpleSearchField searchField; private ToolBar buttonBar; @@ -76,25 +71,22 @@ public class SimpleProductPage extends SimpleInstallerPage implements FilterHand 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); - - GridLayout layout = UIUtil.createGridLayout(1); - layout.verticalSpacing = 20; - setLayout(layout); + super(parent, dialog, false); + } + @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 @@ public class SimpleProductPage extends SimpleInstallerPage implements FilterHand 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 @@ public class SimpleProductPage extends SimpleInstallerPage implements FilterHand } @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 class SimpleProductPage extends SimpleInstallerPage implements FilterHand 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 @@ public class SimpleProductPage extends SimpleInstallerPage implements FilterHand filter = filter.toLowerCase(); } - boolean zebra = true; for (Scope scope : catalogSelector.getSelectedCatalogs()) { if (scope instanceof ProductCatalog) @@ -230,25 +209,16 @@ public class SimpleProductPage extends SimpleInstallerPage implements FilterHand 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 class SimpleProductPage extends SimpleInstallerPage implements FilterHand } } - public static String getHtml(StringBuilder builder) - { - builder.append("</table></body></html>\n"); - return builder.toString(); - } - - public static void renderProduct(StringBuilder builder, Product product, boolean zebra, String downloadImageURI) + private static String removeLinks(String description) { - 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 @@ public class SimpleProductPage extends SimpleInstallerPage implements FilterHand 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 @@ public class SimpleProductPage extends SimpleInstallerPage implements FilterHand 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 000000000..fddf28e98 --- /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 000000000..c441f33ed --- /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 e145adb1d..b5cfbdb3c 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.User; 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.UnsignedContentDialog; 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.IProgressMonitor; 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.graphics.Point; 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 @@ import java.util.Map; */ 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 @@ public class SimpleVariablePage extends SimpleInstallerPage 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 @@ public class SimpleVariablePage extends SimpleInstallerPage 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 @@ public class SimpleVariablePage extends SimpleInstallerPage private StackComposite installStack; - private ToolButton installButton; + private InstallLaunchButton installButton; private String installError; @@ -175,34 +166,34 @@ public class SimpleVariablePage extends SimpleInstallerPage 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) - { - super(parent, style, dialog); + private FlatButton showInstallLogButton; - poolEnabled = PREF_POOL_ENABLED.get(true); - enablePool(poolEnabled); + private Composite errorComposite; - 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); + public SimpleVariablePage(final Composite parent, final SimpleInstallerDialog dialog) + { + super(parent, dialog, true); + } + + @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 @@ public class SimpleVariablePage extends SimpleInstallerPage } }); - // 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 @@ public class SimpleVariablePage extends SimpleInstallerPage } }); - 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 @@ public class SimpleVariablePage extends SimpleInstallerPage } }; - 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 @@ public class SimpleVariablePage extends SimpleInstallerPage } }); - 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,59 +362,72 @@ public class SimpleVariablePage extends SimpleInstallerPage } }); - 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() + new Label(variablesComposite, SWT.NONE); + new Label(variablesComposite, SWT.NONE); + + 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); + + new Label(variablesComposite, SWT.NONE); + new Label(variablesComposite, SWT.NONE); + + 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 public void widgetSelected(SelectionEvent e) { - manageBundlePools(); + installCancel(); } }); - // 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() + GridLayout afterInstallLayout = UIUtil.createGridLayout(1); + afterInstallLayout.verticalSpacing = 3; + + afterInstallComposite = new Composite(installStack, SWT.NONE); + afterInstallComposite.setLayout(afterInstallLayout); + + showReadmeButton = createButton(afterInstallComposite, TEXT_README, null, null); + showReadmeButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - dialog.backSelected(); + if (readmePath != null) + { + java.net.URI readmeURI = new File(installFolder, OS.INSTANCE.getEclipseDir() + "/" + readmePath).toURI(); + dialog.showReadme(readmeURI); + } } }); - installStack = new StackComposite(this, SWT.NONE); - installStack.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - - 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); - cancelButton.addSelectionListener(new SelectionAdapter() + showInstallLogButton = createButton(afterInstallComposite, TEXT_LOG, null, null); + showInstallLogButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - installCancel(); + openInstallLog(); } }); - new Label(this, SWT.NONE); - - installButton = new ToolButton(installStack, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/download_small.png"), false); - - final Composite progressComposite = new Composite(installStack, SWT.NONE); - progressComposite.setLayout(UIUtil.createGridLayout(1)); - - GridData layoutData2 = new GridData(SWT.FILL, SWT.CENTER, true, true); - layoutData2.heightHint = 28; - - 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 @@ public class SimpleVariablePage extends SimpleInstallerPage } 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() - { - @Override - public void widgetSelected(SelectionEvent e) - { - if (TEXT_LAUNCH.equals(e.text)) - { - launchProduct(); - return; - } + errorComposite = new Composite(installStack, SWT.NONE); + errorComposite.setLayout(errorLayout); - 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(); - } + // Just for debugging + // installStack.setVisible(true); + // installStack.setTopControl(errorComposite); + // installButton.setProgress(0.98f); + // installButton.setCurrentState(InstallLaunchButton.State.INSTALLING); + // installButton.setEnabled(false); + } - installButton.setFocus(); - } - }); + 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) + { + button.setImage(icon); + } + + if (toolTip != null) + { + button.setToolTipText(toolTip); + } - 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 @@ public class SimpleVariablePage extends SimpleInstallerPage { 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 @@ public class SimpleVariablePage extends SimpleInstallerPage ++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 @@ public class SimpleVariablePage extends SimpleInstallerPage 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 @@ public class SimpleVariablePage extends SimpleInstallerPage folderText.setEnabled(enabled); folderButton.setEnabled(enabled); - poolButton.setEnabled(enabled); - backButton.setEnabled(enabled); } public boolean refreshJREs() @@ -670,104 +652,6 @@ public class SimpleVariablePage extends SimpleInstallerPage 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 @@ public class SimpleVariablePage extends SimpleInstallerPage 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 @@ public class SimpleVariablePage extends SimpleInstallerPage 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 @@ public class SimpleVariablePage extends SimpleInstallerPage 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.setCurrentState(State.INSTALLED); installButton.setToolTipText("Launch"); - progressLabel.setForeground(getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN)); - message = "Installation finished successfully: <a>" + TEXT_LAUNCH + "</a>\n\n"; + showInstallLogButton.setParent(afterInstallComposite); + keepInstallerButton.setVisible(InstallerUtil.canKeepInstaller()); Scope scope = selectedProductVersion; while (scope != null) @@ -942,7 +819,7 @@ public class SimpleVariablePage extends SimpleInstallerPage readmePath = annotation.getDetails().get(AnnotationConstants.KEY_README_PATH); if (readmePath != null) { - message += "<a>" + TEXT_README + "</a>\n"; + showReadmeButton.setEnabled(true); break; } } @@ -950,28 +827,51 @@ public class SimpleVariablePage extends SimpleInstallerPage 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); + setEnabled(true); + dialog.setButtonsEnabled(true); + } - cancelButton.setVisible(false); - installStack.setTopControl(installButton); + private void showSuccessMessage() + { + dialog.showMessage(MESSAGE_SUCCESS, Type.SUCCESS, true); - dialog.setButtonsEnabled(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() @@ -991,7 +891,6 @@ public class SimpleVariablePage extends SimpleInstallerPage private void setFolderText(String dir) { folderText.setText(dir); - folderText.setSelection(dir.length()); } private void validateFolderText(String dir) @@ -1015,6 +914,17 @@ public class SimpleVariablePage extends SimpleInstallerPage } } + 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 @@ public class SimpleVariablePage extends SimpleInstallerPage /** * @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 @@ public class SimpleVariablePage extends SimpleInstallerPage private volatile boolean done; - private int lastSelection = -1; - private String lastName; public void setTerminating() @@ -1245,24 +1153,18 @@ public class SimpleVariablePage extends SimpleInstallerPage 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 000000000..00aa437b6 --- /dev/null +++ b/plugins/org.eclipse.oomph.ui/src/org/eclipse/oomph/internal/ui/FlatButton.java @@ -0,0 +1,566 @@ +/* + * 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 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.TRANSPARENT); + this.buttonStyle = buttonStyle; + + setLayout(new GridLayout(1, false)); + setBackground(null); + 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++; + } + + drawBackground(gc, clientX, clientY, clientWidth, clientHeight, 0, 0); + drawContent(e); + if (isHover()) + { + drawHoverState(gc, clientX, clientY, clientWidth, clientHeight); + } + } + + 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) + { + 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(); + } + } + + 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 000000000..90ca12cc5 --- /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 000000000..c7fefea83 --- /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 dd37d39c8..95932d8df 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.jface.viewers.Viewer; 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 final class UIUtil { 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 dc8de5f14..be524ef20 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 @@ public final class StringUtil 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; + } } |