| /* |
| * Copyright (c) 2014 Eike Stepper (Berlin, Germany) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.oomph.setup.internal.installer; |
| |
| import org.eclipse.oomph.internal.ui.AccessUtil; |
| import org.eclipse.oomph.setup.Index; |
| import org.eclipse.oomph.setup.Product; |
| import org.eclipse.oomph.setup.ProductCatalog; |
| import org.eclipse.oomph.setup.Scope; |
| import org.eclipse.oomph.setup.SetupPackage; |
| 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; |
| import org.eclipse.oomph.ui.UIUtil; |
| import org.eclipse.oomph.util.OS; |
| import org.eclipse.oomph.util.StringUtil; |
| |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.ecore.EClass; |
| 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.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.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.ToolItem; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.List; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| 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 boolean FANCY = false; // OS.INSTANCE.isWin(); |
| |
| private SearchField searchField; |
| |
| private ToolBar buttonBar; |
| |
| private ToolItem catalogsButton; |
| |
| private CatalogSelector catalogSelector; |
| |
| private StackComposite stackComposite; |
| |
| private Browser browser; |
| |
| public SimpleProductPage(final Composite parent, int style, final SimpleInstallerDialog dialog) |
| { |
| super(parent, style, dialog); |
| |
| GridLayout layout = UIUtil.createGridLayout(1); |
| layout.verticalSpacing = 20; |
| setLayout(layout); |
| |
| GridLayout searchLayout = UIUtil.createGridLayout(2); |
| searchLayout.marginWidth = SimpleInstallerDialog.MARGIN_WIDTH; |
| |
| Composite searchComposite = new Composite(this, 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) |
| { |
| @Override |
| protected void finishFilter() |
| { |
| browser.setFocus(); |
| } |
| }; |
| searchField.setLayoutData(searchFieldData); |
| searchField.getFilterControl().setFont(font); |
| |
| buttonBar = new ToolBar(searchComposite, SWT.FLAT | SWT.RIGHT); |
| buttonBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| |
| 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)); |
| |
| final SpriteIndexLoader indexLoader = new SpriteIndexLoader(stackComposite); |
| |
| browser = new Browser(stackComposite, SWT.NONE); |
| browser.addLocationListener(new LocationAdapter() |
| { |
| @Override |
| public void changing(LocationEvent event) |
| { |
| String url = event.location; |
| if (!"about:blank".equals(url)) |
| { |
| if (url.startsWith(PRODUCT_PREFIX)) |
| { |
| indexLoader.awaitIndexLoad(); |
| |
| url = url.substring(PRODUCT_PREFIX.length()); |
| int lastSlash = url.lastIndexOf('/'); |
| |
| String catalogName = url.substring(0, lastSlash); |
| String productName = url.substring(lastSlash + 1); |
| |
| for (Scope scope : catalogSelector.getSelectedCatalogs()) |
| { |
| if (scope instanceof ProductCatalog) |
| { |
| ProductCatalog productCatalog = (ProductCatalog)scope; |
| if (catalogName.equals(productCatalog.getName())) |
| { |
| for (Product product : productCatalog.getProducts()) |
| { |
| if (productName.equals(product.getName())) |
| { |
| dialog.productSelected(product); |
| } |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| OS.INSTANCE.openSystemBrowser(url); |
| } |
| |
| event.doit = false; |
| } |
| } |
| }); |
| |
| stackComposite.setTopControl(indexLoader.getAnimator()); |
| |
| installer.setIndexLoader(indexLoader); |
| installer.setIndexLoadedAction(new Runnable() |
| { |
| public void run() |
| { |
| handleFilter(""); |
| } |
| }); |
| } |
| |
| @Override |
| public boolean setFocus() |
| { |
| return browser.setFocus(); |
| } |
| |
| public void handleFilter(String filter) |
| { |
| String filterText = searchField.getFilterControl().getText(); |
| 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"); |
| |
| boolean noFilter = StringUtil.isEmpty(filter); |
| if (!noFilter) |
| { |
| filter = filter.toLowerCase(); |
| } |
| |
| boolean zebra = true; |
| for (Scope scope : catalogSelector.getSelectedCatalogs()) |
| { |
| if (scope instanceof ProductCatalog) |
| { |
| ProductCatalog productCatalog = (ProductCatalog)scope; |
| for (Product product : productCatalog.getProducts()) |
| { |
| 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; |
| } |
| } |
| } |
| } |
| |
| String html = getHtml(builder); |
| |
| // try |
| // { |
| // IOUtil.writeUTF8(new File("/develop/products.html"), html); |
| // } |
| // catch (Exception ex) |
| // { |
| // ex.printStackTrace(); |
| // } |
| |
| browser.setText(html, true); |
| } |
| |
| public void reset() |
| { |
| browser.setText(browser.getText()); |
| } |
| |
| private void createCatalogsButton() |
| { |
| if (catalogsButton == null) |
| { |
| catalogsButton = new ToolItem(buttonBar, SWT.DROP_DOWN); |
| catalogsButton.setToolTipText("Select Catalogs"); |
| catalogsButton.setImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png")); |
| |
| CatalogManager catalogManager = catalogSelector.getCatalogManager(); |
| catalogManager.getSelection().eAdapters().add(new AdapterImpl() |
| { |
| @Override |
| public void notifyChanged(Notification msg) |
| { |
| handleFilter(""); |
| } |
| }); |
| |
| catalogSelector.configure(catalogsButton); |
| AccessUtil.setKey(catalogsButton, "catalogs"); |
| } |
| } |
| |
| private void disposeCatalogsButton() |
| { |
| if (catalogsButton != null) |
| { |
| catalogsButton.dispose(); |
| catalogsButton = null; |
| } |
| } |
| |
| 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) |
| { |
| 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"); |
| } |
| |
| private static int findFirstDot(String description) |
| { |
| boolean inElement = false; |
| for (int i = 0; i < description.length(); i++) |
| { |
| char c = description.charAt(i); |
| if (inElement) |
| { |
| if (c == '>') |
| { |
| inElement = false; |
| } |
| } |
| else |
| { |
| if (c == '<') |
| { |
| inElement = true; |
| } |
| else if (c == '.') |
| { |
| return i; |
| } |
| } |
| } |
| |
| return -1; |
| } |
| |
| private static boolean isFiltered(String string, String filter) |
| { |
| if (string == null) |
| { |
| return false; |
| } |
| |
| return string.toLowerCase().contains(filter); |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private final class SpriteIndexLoader extends IndexLoader |
| { |
| private final SpriteAnimator animator; |
| |
| public SpriteIndexLoader(Composite parent) |
| { |
| animator = new SpriteAnimator(parent, SWT.NO_BACKGROUND, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/progress_sprite.png"), 32, 32, 20); |
| } |
| |
| public SpriteAnimator getAnimator() |
| { |
| return animator; |
| } |
| |
| @Override |
| public void loadIndex(final ResourceSet resourceSet, final org.eclipse.emf.common.util.URI... uris) |
| { |
| searchField.setEnabled(false); |
| |
| if (catalogsButton != null) |
| { |
| catalogsButton.setEnabled(false); |
| } |
| |
| browser.setText("", true); |
| stackComposite.setTopControl(animator); |
| animator.start(1, animator.getImages().length - 1); |
| |
| final IProgressMonitor monitor = new NullProgressMonitor(); |
| |
| Thread thread = new Thread() |
| { |
| @Override |
| public void run() |
| { |
| try |
| { |
| loadIndex(resourceSet, uris, monitor); |
| } |
| catch (InvocationTargetException ex) |
| { |
| if (!animator.isDisposed()) |
| { |
| SetupInstallerPlugin.INSTANCE.log(ex.getCause()); |
| } |
| } |
| catch (InterruptedException ex) |
| { |
| //$FALL-THROUGH$ |
| } |
| finally |
| { |
| UIUtil.asyncExec(new Runnable() |
| { |
| public void run() |
| { |
| stackComposite.setTopControl(browser); |
| browser.setFocus(); |
| |
| CatalogManager catalogManager = catalogSelector.getCatalogManager(); |
| Index index = catalogManager.getIndex(); |
| if (index == null) |
| { |
| disposeCatalogsButton(); |
| int answer = new MessageDialog(getShell(), "Network Problem", null, |
| "The catalog could not be loaded. Please ensure that you have network access and, if needed, have configured your network proxy.", |
| MessageDialog.ERROR, new String[] { "Retry", "Configure Network Proxy...", "Exit" }, 0).open(); |
| switch (answer) |
| { |
| case 0: |
| installer.reloadIndex(); |
| return; |
| |
| case 1: |
| new ProxyPreferenceDialog(getShell()).open(); |
| installer.reloadIndex(); |
| return; |
| |
| default: |
| dialog.exitSelected(); |
| return; |
| } |
| } |
| |
| searchField.setEnabled(true); |
| |
| List<? extends Scope> productCatalogs = catalogManager.getCatalogs(true); |
| if (productCatalogs != null && productCatalogs.size() >= 3) // Self products + 2 more catalogs |
| { |
| createCatalogsButton(); |
| catalogsButton.setEnabled(true); |
| } |
| else |
| { |
| disposeCatalogsButton(); |
| } |
| |
| buttonBar.getParent().layout(); |
| } |
| }); |
| |
| animator.stop(); |
| } |
| } |
| }; |
| |
| thread.setDaemon(true); |
| thread.start(); |
| } |
| |
| @Override |
| protected void waiting() |
| { |
| stackComposite.setTopControl(animator); |
| animator.start(1, animator.getImages().length - 1); |
| } |
| |
| @Override |
| protected void finishedWaiting() |
| { |
| stackComposite.setTopControl(browser); |
| } |
| |
| @Override |
| protected boolean shouldReload(EClass eClass) |
| { |
| return eClass == SetupPackage.Literals.INDEX || eClass == SetupPackage.Literals.PRODUCT_CATALOG || eClass == SetupPackage.Literals.PRODUCT; |
| } |
| } |
| } |