/******************************************************************************* * Copyright (c) 2009 Tasktop Technologies 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: * Tasktop Technologies - initial API and implementation * Obeo - adaptation for Amalgamation, EMF based and no Mylyn dependency * CEA LIST - adaptation for Papyrus extra components needs *******************************************************************************/ package org.eclipse.papyrus.infra.discovery.ui.viewer; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.DecorationOverlayIcon; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.window.IShellProvider; import org.eclipse.osgi.util.NLS; import org.eclipse.papyrus.infra.discovery.Category; import org.eclipse.papyrus.infra.discovery.DiscoveryDefinition; import org.eclipse.papyrus.infra.discovery.Group; import org.eclipse.papyrus.infra.discovery.InstallableComponent; import org.eclipse.papyrus.infra.discovery.Message; import org.eclipse.papyrus.infra.discovery.Overview; import org.eclipse.papyrus.infra.discovery.Severity; import org.eclipse.papyrus.infra.discovery.ui.Activator; import org.eclipse.papyrus.infra.discovery.ui.internal.common.CommonImages; import org.eclipse.papyrus.infra.discovery.ui.internal.common.CommonThemes; import org.eclipse.papyrus.infra.discovery.ui.internal.common.DiscoveryImages; import org.eclipse.papyrus.infra.discovery.ui.internal.common.GradientCanvas; import org.eclipse.papyrus.infra.discovery.ui.internal.common.OverviewToolTip; import org.eclipse.papyrus.infra.discovery.ui.internal.common.SelectionProviderAdapter; import org.eclipse.papyrus.infra.discovery.ui.internal.common.WorkbenchUtil; import org.eclipse.papyrus.infra.discovery.ui.internal.viewer.DiscoveryCategoryComparator; import org.eclipse.papyrus.infra.discovery.ui.internal.wizards.Messages; import org.eclipse.papyrus.infra.discovery.ui.wizards.DiscoveryWizard; import org.eclipse.swt.SWT; import org.eclipse.swt.accessibility.ACC; import org.eclipse.swt.accessibility.AccessibleAdapter; import org.eclipse.swt.accessibility.AccessibleControlAdapter; import org.eclipse.swt.accessibility.AccessibleControlEvent; import org.eclipse.swt.accessibility.AccessibleEvent; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseAdapter; 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.events.PaintEvent; import org.eclipse.swt.events.PaintListener; 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.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; import org.eclipse.ui.forms.IFormColors; import org.eclipse.ui.progress.WorkbenchJob; import org.eclipse.ui.themes.IThemeManager; /** * The main wizard page that allows users to select connectors that they wish to * install. * * @author David Green * @author Steffen Pingel * @author Cedric Brun * @author Remi Schnekenburger */ public class DiscoveryViewer { public class ConnectorBorderPaintListener implements PaintListener { public void paintControl(PaintEvent e) { Composite composite = (Composite) e.widget; Rectangle bounds = composite.getBounds(); GC gc = e.gc; gc.setLineStyle(SWT.LINE_DOT); gc.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y); } } private class ConnectorDescriptorItemUi extends AdapterImpl implements Runnable { private final Button checkbox; private final Label iconLabel; private final Label nameLabel; private ToolItem infoButton; private final Link providerLabel; private final Label description; private final Composite checkboxContainer; private final Composite connectorContainer; private final Display display; private Image iconImage; private Image warningIconImage; private InstallableComponent connector; public ConnectorDescriptorItemUi(final InstallableComponent connector, Composite categoryChildrenContainer, Color background) { display = categoryChildrenContainer.getDisplay(); this.connector = connector; connector.eAdapters().add(this); connectorContainer = new Composite(categoryChildrenContainer, SWT.NULL); configureLook(connectorContainer, background); GridDataFactory.fillDefaults().grab(true, false).applyTo( connectorContainer); GridLayout layout = new GridLayout(4, false); layout.marginLeft = 7; layout.marginTop = 2; layout.marginBottom = 2; connectorContainer.setLayout(layout); checkboxContainer = new Composite(connectorContainer, SWT.NULL); configureLook(checkboxContainer, background); GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.BEGINNING) .span(1, 2).applyTo(checkboxContainer); GridLayoutFactory.fillDefaults().spacing(1, 1).numColumns(2) .applyTo(checkboxContainer); checkbox = new Button(checkboxContainer, SWT.CHECK); checkbox.setText(" "); //$NON-NLS-1$ // help UI tests checkbox.setData("connectorId", connector.getId()); //$NON-NLS-1$ configureLook(checkbox, background); checkbox.setSelection(installableConnectors.contains(connector)); checkbox.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { bodyScrolledComposite.showControl(connectorContainer); } }); GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.CENTER) .applyTo(checkbox); iconLabel = new Label(checkboxContainer, SWT.NULL); configureLook(iconLabel, background); GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.CENTER) .applyTo(iconLabel); if (connector.getImage32() != null) { iconImage = computeIconImage(connector.eResource(), connector .getImage32(), 32, false); if (iconImage != null) { if (connector.isIncubation()) { iconImage = new DecorationOverlayIcon(iconImage, DiscoveryImages.OVERLAY_INCUBATION_32, IDecoration.BOTTOM_LEFT).createImage(); disposables.add(iconImage); } iconLabel.setImage(iconImage); } } nameLabel = new Label(connectorContainer, SWT.NULL); configureLook(nameLabel, background); GridDataFactory.fillDefaults().grab(true, false).align( SWT.BEGINNING, SWT.CENTER).applyTo(nameLabel); nameLabel.setFont(h2Font); if (connector.isIncubation()) nameLabel.setText(connector.getName() + " (Incubation)"); else nameLabel.setText(connector.getName()); providerLabel = new Link(connectorContainer, SWT.RIGHT); configureLook(providerLabel, background); GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo( providerLabel); providerLabel .setText(NLS .bind( Messages.ConnectorDiscoveryWizardMainPage_provider_and_license, connector.getProvider(), connector .getLicense())); if (hasTooltip(connector)) { ToolBar toolBar = new ToolBar(connectorContainer, SWT.FLAT); toolBar.setBackground(background); infoButton = new ToolItem(toolBar, SWT.PUSH); infoButton.setImage(infoImage); infoButton .setToolTipText(Messages.ConnectorDiscoveryWizardMainPage_tooltip_showOverview); hookTooltip(toolBar, infoButton, connectorContainer, nameLabel, connector.eResource(), connector.getOverview(), null); GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER) .applyTo(toolBar); } else { Label label = new Label(connectorContainer, SWT.NULL); label.setText(" "); //$NON-NLS-1$ configureLook(label, background); } description = new Label(connectorContainer, SWT.NULL | SWT.WRAP); configureLook(description, background); GridDataFactory.fillDefaults().grab(true, false).span(3, 1).hint( 100, SWT.DEFAULT).applyTo(description); String descriptionText = connector.getDescription(); if (descriptionText != null) { int maxDescriptionLength = 162; if (descriptionText.length() > maxDescriptionLength) { descriptionText = descriptionText.substring(0, maxDescriptionLength); } description.setText(descriptionText.replaceAll( "(\\r\\n)|\\n|\\r", " ")); //$NON-NLS-1$ //$NON-NLS-2$ } // always disabled color to make it less prominent providerLabel.setForeground(colorDisabled); checkbox.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } public void widgetSelected(SelectionEvent e) { boolean selected = checkbox.getSelection(); maybeModifySelection(selected); } }); MouseListener connectorItemMouseListener = new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { boolean selected = !checkbox.getSelection(); if (maybeModifySelection(selected)) { checkbox.setSelection(selected); } } }; checkboxContainer.addMouseListener(connectorItemMouseListener); connectorContainer.addMouseListener(connectorItemMouseListener); iconLabel.addMouseListener(connectorItemMouseListener); nameLabel.addMouseListener(connectorItemMouseListener); // the provider has clickable links // providerLabel.addMouseListener(connectorItemMouseListener); providerLabel.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { if (e.text != null) { WorkbenchUtil.openUrl(e.text, IWorkbenchBrowserSupport.AS_EXTERNAL); } } public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); description.addMouseListener(connectorItemMouseListener); } protected boolean maybeModifySelection(boolean selected) { if (selected) { if (!connector.isAvailable()) { MessageDialog .openWarning( shellProvider.getShell(), Messages.ConnectorDiscoveryWizardMainPage_warningTitleConnectorUnavailable, NLS .bind( Messages.ConnectorDiscoveryWizardMainPage_warningMessageConnectorUnavailable, connector.getName())); return false; } } DiscoveryViewer.this.modifySelection(connector, selected); return true; } @Override public void notifyChanged(Notification msg) { super.notifyChanged(msg); display.asyncExec(this); } public void run() { if (!connectorContainer.isDisposed()) { updateAvailability(); } } public void updateAvailability() { boolean enabled = connector.isAvailable(); checkbox.setEnabled(enabled); nameLabel.setEnabled(enabled); providerLabel.setEnabled(enabled); description.setEnabled(enabled); Color foreground; if (enabled) { foreground = connectorContainer.getForeground(); } else { foreground = colorDisabled; } nameLabel.setForeground(foreground); description.setForeground(foreground); if (iconImage != null) { boolean unavailable = !enabled; if (unavailable) { if (warningIconImage == null) { warningIconImage = new DecorationOverlayIcon(iconImage, DiscoveryImages.OVERLAY_WARNING_32, IDecoration.BOTTOM_RIGHT).createImage(); disposables.add(warningIconImage); } iconLabel.setImage(warningIconImage); } else if (warningIconImage != null) { iconLabel.setImage(iconImage); } } } } // e3.5 replace with SWT.ICON_CANCEL public static final int ICON_CANCEL = 1 << 8; private static final int MINIMUM_HEIGHT = 100; private static boolean useNativeSearchField(Composite composite) { if (useNativeSearchField == null) { useNativeSearchField = Boolean.FALSE; Text testText = null; try { testText = new Text(composite, SWT.SEARCH | ICON_CANCEL); useNativeSearchField = new Boolean( (testText.getStyle() & ICON_CANCEL) != 0); } finally { if (testText != null) { testText.dispose(); } } } return useNativeSearchField; } private boolean showConnectorDescriptorKindFilter; private boolean showConnectorDescriptorTextFilter; private static final String COLOR_WHITE = "white"; //$NON-NLS-1$ private static final String COLOR_DARK_GRAY = "DarkGray"; //$NON-NLS-1$ private static Boolean useNativeSearchField; private final List<InstallableComponent> installableConnectors = new ArrayList<InstallableComponent>(); private Composite body; private final List<org.eclipse.swt.graphics.Resource> disposables; private Font h2Font; private Font h1Font; private Color colorWhite; private Text filterText; private WorkbenchJob refreshJob; private String previousFilterText = ""; //$NON-NLS-1$ private Pattern filterPattern; private Label clearFilterTextControl; private Image infoImage; private Cursor handCursor; private Color colorCategoryGradientStart; private Color colorCategoryGradientEnd; private Color colorDisabled; private ScrolledComposite bodyScrolledComposite; private boolean verifyUpdateSiteAvailability; private final Map<Group, Boolean> connectorDescriptorKindToVisibility = new HashMap<Group, Boolean>(); { } private boolean complete; private final IRunnableContext context; private final IShellProvider shellProvider; private Control control; private final SelectionProviderAdapter selectionProvider; private List<InstallableComponent> allConnectors; private int minimumHeight; private DiscoveryContentProvider provider; public DiscoveryViewer(IShellProvider shellProvider, IRunnableContext context, DiscoveryContentProvider provider) { this.shellProvider = shellProvider; this.context = context; this.selectionProvider = new SelectionProviderAdapter(); this.allConnectors = Collections.emptyList(); this.disposables = new ArrayList<org.eclipse.swt.graphics.Resource>(); this.provider = provider; setShowConnectorDescriptorKindFilter(true); setShowConnectorDescriptorTextFilter(true); setMinimumHeight(MINIMUM_HEIGHT); setComplete(false); for (Group kind : getDiscovery().getFilters()) { connectorDescriptorKindToVisibility.put(kind, true); } ; } public void addSelectionChangedListener(ISelectionChangedListener listener) { selectionProvider.addSelectionChangedListener(listener); } private void clearDisposables() { disposables.clear(); h1Font = null; h2Font = null; infoImage = null; handCursor = null; colorCategoryGradientStart = null; colorCategoryGradientEnd = null; } private void clearFilterText() { filterText.setText(""); //$NON-NLS-1$ filterTextChanged(); } private Image computeIconImage(Resource discoverySource, String imagePath, int dimension, boolean fallback) { if (imagePath != null && imagePath.length() > 0) { URI uri = discoverySource.getURI(); URI trimed = uri.trimSegments(1); String urlPath = trimed.toString() + "/" + imagePath; URL resource = null; try { resource = new URL(urlPath); } catch (MalformedURLException e) { // TODO Auto-generated catch block } if (resource != null) { ImageDescriptor descriptor = ImageDescriptor .createFromURL(resource); Image image = descriptor.createImage(); if (image != null) { disposables.add(image); return image; } } } return null; } private void configureLook(Control control, Color background) { control.setBackground(background); } /** * cause the UI to respond to a change in visibility filters * * @see #setVisibility(ConnectorDescriptorKind, boolean) */ public void connectorDescriptorKindVisibilityUpdated() { createBodyContents(); } public void createBodyContents() { // remove any existing contents for (Control child : body.getChildren()) { child.dispose(); } clearDisposables(); allConnectors = new ArrayList<InstallableComponent>(); initializeCursors(); initializeImages(); initializeFonts(); initializeColors(); GridLayoutFactory.fillDefaults().applyTo(body); bodyScrolledComposite = new ScrolledComposite(body, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); configureLook(bodyScrolledComposite, colorWhite); GridDataFactory.fillDefaults().grab(true, true).applyTo( bodyScrolledComposite); final Composite scrolledContents = new Composite(bodyScrolledComposite, SWT.NONE); configureLook(scrolledContents, colorWhite); scrolledContents.setRedraw(false); try { createDiscoveryContents(scrolledContents); } finally { scrolledContents.layout(true); scrolledContents.setRedraw(true); } Point size = scrolledContents.computeSize(body.getSize().x, SWT.DEFAULT, true); scrolledContents.setSize(size); bodyScrolledComposite.setExpandHorizontal(true); bodyScrolledComposite.setMinWidth(100); bodyScrolledComposite.setExpandVertical(true); bodyScrolledComposite.setMinHeight(1); bodyScrolledComposite.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { // XXX small offset in case list has a scroll bar Point size = scrolledContents.computeSize( body.getSize().x - 20, SWT.DEFAULT, true); scrolledContents.setSize(size); bodyScrolledComposite.setMinHeight(size.y); } }); bodyScrolledComposite.setContent(scrolledContents); Dialog.applyDialogFont(body); // we've changed it so it needs to know body.layout(true); } private Label createClearFilterTextControl(Composite filterContainer, final Text filterText) { final Image inactiveImage = CommonImages.FIND_CLEAR_DISABLED .createImage(); final Image activeImage = CommonImages.FIND_CLEAR.createImage(); final Image pressedImage = new Image(filterContainer.getDisplay(), activeImage, SWT.IMAGE_GRAY); final Label clearButton = new Label(filterContainer, SWT.NONE); clearButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); clearButton.setImage(inactiveImage); clearButton .setToolTipText(Messages.ConnectorDiscoveryWizardMainPage_clearButton_toolTip); clearButton.setBackground(filterContainer.getDisplay().getSystemColor( SWT.COLOR_LIST_BACKGROUND)); clearButton.addMouseListener(new MouseAdapter() { private MouseMoveListener fMoveListener; private boolean isMouseInButton(MouseEvent e) { Point buttonSize = clearButton.getSize(); return 0 <= e.x && e.x < buttonSize.x && 0 <= e.y && e.y < buttonSize.y; } @Override public void mouseDown(MouseEvent e) { clearButton.setImage(pressedImage); fMoveListener = new MouseMoveListener() { private boolean fMouseInButton = true; public void mouseMove(MouseEvent e) { boolean mouseInButton = isMouseInButton(e); if (mouseInButton != fMouseInButton) { fMouseInButton = mouseInButton; clearButton.setImage(mouseInButton ? pressedImage : inactiveImage); } } }; clearButton.addMouseMoveListener(fMoveListener); } @Override public void mouseUp(MouseEvent e) { if (fMoveListener != null) { clearButton.removeMouseMoveListener(fMoveListener); fMoveListener = null; boolean mouseInButton = isMouseInButton(e); clearButton.setImage(mouseInButton ? activeImage : inactiveImage); if (mouseInButton) { clearFilterText(); filterText.setFocus(); } } } }); clearButton.addMouseTrackListener(new MouseTrackListener() { public void mouseEnter(MouseEvent e) { clearButton.setImage(activeImage); } public void mouseExit(MouseEvent e) { clearButton.setImage(inactiveImage); } public void mouseHover(MouseEvent e) { } }); clearButton.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { inactiveImage.dispose(); activeImage.dispose(); pressedImage.dispose(); } }); clearButton.getAccessible().addAccessibleListener( new AccessibleAdapter() { @Override public void getName(AccessibleEvent e) { e.result = Messages.ConnectorDiscoveryWizardMainPage_clearButton_accessibleListener; } }); clearButton.getAccessible().addAccessibleControlListener( new AccessibleControlAdapter() { @Override public void getRole(AccessibleControlEvent e) { e.detail = ACC.ROLE_PUSHBUTTON; } }); return clearButton; } public void createControl(Composite parent) { createRefreshJob(); Composite container = new Composite(parent, SWT.NULL); container.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { refreshJob.cancel(); } }); GridLayout layout = new GridLayout(1, false); layout.marginHeight = 0; layout.marginWidth = 0; container.setLayout(layout); // { // header Composite header = new Composite(container, SWT.NULL); GridLayoutFactory.fillDefaults().applyTo(header); GridDataFactory.fillDefaults().grab(true, false).applyTo(header); // TODO: refresh button? if (isShowConnectorDescriptorKindFilter() || isShowConnectorDescriptorTextFilter()) { Composite filterContainer = new Composite(header, SWT.NULL); GridDataFactory.fillDefaults().grab(true, false).applyTo( filterContainer); int numColumns = 1; // 1 for label if (isShowConnectorDescriptorKindFilter()) { numColumns += getDiscovery().getFilters().size(); } if (isShowConnectorDescriptorTextFilter()) { ++numColumns; } GridLayoutFactory.fillDefaults().numColumns(numColumns) .applyTo(filterContainer); Label label = new Label(filterContainer, SWT.NULL); label .setText(Messages.ConnectorDiscoveryWizardMainPage_filterLabel); if (isShowConnectorDescriptorTextFilter()) { Composite textFilterContainer; boolean nativeSearch = useNativeSearchField(header); if (nativeSearch) { textFilterContainer = new Composite(filterContainer, SWT.NULL); } else { textFilterContainer = new Composite(filterContainer, SWT.BORDER); textFilterContainer.setBackground(header.getDisplay() .getSystemColor(SWT.COLOR_LIST_BACKGROUND)); } GridDataFactory.fillDefaults().grab(true, false).applyTo( textFilterContainer); GridLayoutFactory.fillDefaults().numColumns(2).applyTo( textFilterContainer); if (nativeSearch) { filterText = new Text(textFilterContainer, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | ICON_CANCEL); } else { filterText = new Text(textFilterContainer, SWT.SINGLE); } filterText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { filterTextChanged(); } }); if (nativeSearch) { filterText.addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { if (e.detail == ICON_CANCEL) { clearFilterText(); } } }); GridDataFactory.fillDefaults().grab(true, false).span( 2, 1).applyTo(filterText); } else { GridDataFactory.fillDefaults().grab(true, false) .applyTo(filterText); clearFilterTextControl = createClearFilterTextControl( textFilterContainer, filterText); clearFilterTextControl.setVisible(false); } } if (isShowConnectorDescriptorKindFilter()) { // filter // buttons for (final Group kind : getDiscovery().getFilters()) { final Button checkbox = new Button(filterContainer, SWT.CHECK); checkbox.setSelection(isVisible(kind)); checkbox.setText(getFilterLabel(kind)); checkbox.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } public void widgetSelected(SelectionEvent e) { boolean selection = checkbox.getSelection(); setVisibility(kind, selection); connectorDescriptorKindVisibilityUpdated(); } }); } } } } { // container body = new Composite(container, SWT.NULL); GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, minimumHeight).applyTo(body); } Dialog.applyDialogFont(container); setControl(container); } public void setMinimumHeight(int minimumHeight) { this.minimumHeight = minimumHeight; if (body != null) { GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, minimumHeight).applyTo(body); } } public static int getMinimumHeight() { return MINIMUM_HEIGHT; } private void createDiscoveryContents(Composite container) { Color background = container.getBackground(); if (provider.getDiscovery() == null || isEmpty(provider.getDiscovery())) { GridLayoutFactory.fillDefaults().margins(5, 5).applyTo(container); boolean atLeastOneKindFiltered = false; for (Group kind : getDiscovery().getFilters()) { if (!isVisible(kind)) { atLeastOneKindFiltered = true; break; } } Control helpTextControl; if (filterPattern != null) { Link link = new Link(container, SWT.WRAP); link.setFont(container.getFont()); link .setText(Messages.ConnectorDiscoveryWizardMainPage_noMatchingItems_withFilterText); link.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { clearFilterText(); filterText.setFocus(); } }); helpTextControl = link; } else { Label helpText = new Label(container, SWT.WRAP); helpText.setFont(container.getFont()); if (atLeastOneKindFiltered) { helpText .setText(Messages.ConnectorDiscoveryWizardMainPage_noMatchingItems_filteredType); } else { helpText .setText(Messages.ConnectorDiscoveryWizardMainPage_noMatchingItems_noFilter); } helpTextControl = helpText; } configureLook(helpTextControl, background); GridDataFactory.fillDefaults().grab(true, false).hint(100, SWT.DEFAULT).applyTo(helpTextControl); } else { GridLayoutFactory.fillDefaults().numColumns(2).spacing(0, 0) .applyTo(container); List<Category> categories = new ArrayList<Category>(provider .getDiscovery().getCategories()); Collections.sort(categories, new DiscoveryCategoryComparator()); Composite categoryChildrenContainer = null; for (Category category : categories) { if (isEmpty(category)) { // don't add empty categories continue; } { // category header final GradientCanvas categoryHeaderContainer = new GradientCanvas( container, SWT.NONE); categoryHeaderContainer.setSeparatorVisible(true); categoryHeaderContainer.setSeparatorAlignment(SWT.TOP); categoryHeaderContainer .setBackgroundGradient(new Color[] { colorCategoryGradientStart, colorCategoryGradientEnd }, new int[] { 100 }, true); categoryHeaderContainer.putColor( IFormColors.H_BOTTOM_KEYLINE1, colorCategoryGradientStart); categoryHeaderContainer.putColor( IFormColors.H_BOTTOM_KEYLINE2, colorCategoryGradientEnd); GridDataFactory.fillDefaults().span(2, 1).applyTo( categoryHeaderContainer); GridLayoutFactory.fillDefaults().numColumns(3) .margins(5, 5).equalWidth(false).applyTo( categoryHeaderContainer); Label iconLabel = new Label(categoryHeaderContainer, SWT.NULL); if (category.getImage48() != null) { Image image = computeIconImage(category.eResource(), category.getImage48(), 48, true); if (image != null) { iconLabel.setImage(image); } } iconLabel.setBackground(null); GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.BEGINNING).span(1, 2).applyTo(iconLabel); Label nameLabel = new Label(categoryHeaderContainer, SWT.NULL); nameLabel.setFont(h1Font); nameLabel.setText(category.getName()); nameLabel.setBackground(null); GridDataFactory.fillDefaults().grab(true, false).applyTo( nameLabel); if (hasTooltip(category)) { ToolBar toolBar = new ToolBar(categoryHeaderContainer, SWT.FLAT); toolBar.setBackground(null); ToolItem infoButton = new ToolItem(toolBar, SWT.PUSH); infoButton.setImage(infoImage); infoButton .setToolTipText(Messages.ConnectorDiscoveryWizardMainPage_tooltip_showOverview); hookTooltip(toolBar, infoButton, categoryHeaderContainer, nameLabel, category .eResource(), category.getOverview(), null); GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(toolBar); } else { new Label(categoryHeaderContainer, SWT.NULL) .setText(" "); //$NON-NLS-1$ } Label description = new Label(categoryHeaderContainer, SWT.WRAP); GridDataFactory.fillDefaults().grab(true, false).span(2, 1) .hint(100, SWT.DEFAULT).applyTo(description); description.setBackground(null); if (category.getDescription() != null) description.setText(category.getDescription()); } categoryChildrenContainer = new Composite(container, SWT.NULL); configureLook(categoryChildrenContainer, background); GridDataFactory.fillDefaults().span(2, 1).grab(true, false) .applyTo(categoryChildrenContainer); GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo( categoryChildrenContainer); int numChildren = 0; List<InstallableComponent> connectors = new ArrayList<InstallableComponent>( category.getComponents()); for (final InstallableComponent connector : connectors) { if (isFiltered(connector)) { continue; } if (++numChildren > 1) { // a separator between connector descriptors Composite border = new Composite( categoryChildrenContainer, SWT.NULL); GridDataFactory.fillDefaults().grab(true, false).hint( SWT.DEFAULT, 1).applyTo(border); GridLayoutFactory.fillDefaults().applyTo(border); border .addPaintListener(new ConnectorBorderPaintListener()); } ConnectorDescriptorItemUi itemUi = new ConnectorDescriptorItemUi( connector, categoryChildrenContainer, background); itemUi.updateAvailability(); allConnectors.add(connector); } } // last one gets a border Composite border = new Composite(categoryChildrenContainer, SWT.NULL); GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 1).applyTo(border); GridLayoutFactory.fillDefaults().applyTo(border); border.addPaintListener(new ConnectorBorderPaintListener()); } container.layout(true); container.redraw(); } protected Pattern createPattern(String filterText) { if (filterText == null || filterText.length() == 0) { return null; } String regex = filterText; regex.replace("\\", "\\\\").replace("?", ".").replace("*", ".*?"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ return Pattern .compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL); } private void createRefreshJob() { refreshJob = new WorkbenchJob("filter") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (filterText.isDisposed()) { return Status.CANCEL_STATUS; } String text = filterText.getText(); text = text.trim(); if (!previousFilterText.equals(text)) { previousFilterText = text; filterPattern = createPattern(previousFilterText); if (clearFilterTextControl != null) { clearFilterTextControl .setVisible(filterPattern != null); } createBodyContents(); } return Status.OK_STATUS; } }; refreshJob.setSystem(true); } private void discoveryUpdated(boolean wasCancelled) { createBodyContents(); if (!wasCancelled) { int categoryWithConnectorCount = 0; for (Category category : provider.getDiscovery().getCategories()) { categoryWithConnectorCount += category.getComponents().size(); } if (categoryWithConnectorCount == 0) { // nothing was discovered: notify the user MessageDialog .openWarning( getShell(), Messages.ConnectorDiscoveryWizardMainPage_noConnectorsFound, Messages.ConnectorDiscoveryWizardMainPage_noConnectorsFound_description); } } selectionProvider.setSelection(StructuredSelection.EMPTY); } public void dispose() { for (org.eclipse.swt.graphics.Resource resource : disposables) { resource.dispose(); } clearDisposables(); } private boolean filterMatches(String text) { return text != null && filterPattern.matcher(text).find(); } private void filterTextChanged() { refreshJob.cancel(); refreshJob.schedule(200L); } public Control getControl() { return control; } public DiscoveryDefinition getDiscovery() { return provider.getDiscovery(); } private String getFilterLabel(Group kind) { return kind.getName(); } public List<InstallableComponent> getInstallableConnectors() { return installableConnectors; } public IStructuredSelection getSelection() { return (IStructuredSelection) selectionProvider.getSelection(); } private Shell getShell() { return shellProvider.getShell(); } public boolean getVerifyUpdateSiteAvailability() { return verifyUpdateSiteAvailability; } private boolean hasTooltip(final Category category) { return category.getOverview() != null && category.getOverview().getSummary() != null && category.getOverview().getSummary().length() > 0; } private boolean hasTooltip(final InstallableComponent connector) { return connector.getOverview() != null && connector.getOverview().getSummary() != null && connector.getOverview().getSummary().length() > 0; } private void hookRecursively(Control control, Listener listener) { control.addListener(SWT.Dispose, listener); control.addListener(SWT.MouseHover, listener); control.addListener(SWT.MouseMove, listener); control.addListener(SWT.MouseExit, listener); control.addListener(SWT.MouseDown, listener); control.addListener(SWT.MouseWheel, listener); if (control instanceof Composite) { for (Control child : ((Composite) control).getChildren()) { hookRecursively(child, listener); } } } private void hookTooltip(final Control parent, final Widget tipActivator, final Control exitControl, final Control titleControl, Resource source, Overview overview, Image image) { final OverviewToolTip toolTip = new OverviewToolTip(parent, source, overview, image); Listener listener = new Listener() { public void handleEvent(Event event) { switch (event.type) { case SWT.MouseHover: toolTip.show(titleControl); break; case SWT.Dispose: case SWT.MouseWheel: toolTip.hide(); break; } } }; tipActivator.addListener(SWT.Dispose, listener); tipActivator.addListener(SWT.MouseWheel, listener); if (image != null) { tipActivator.addListener(SWT.MouseHover, listener); } Listener selectionListener = new Listener() { public void handleEvent(Event event) { toolTip.show(titleControl); } }; tipActivator.addListener(SWT.Selection, selectionListener); Listener exitListener = new Listener() { public void handleEvent(Event event) { switch (event.type) { case SWT.MouseWheel: toolTip.hide(); break; case SWT.MouseExit: /* * Check if the mouse exit happened because we move over the * tooltip */ Rectangle containerBounds = exitControl.getBounds(); Point displayLocation = exitControl.getParent().toDisplay( containerBounds.x, containerBounds.y); containerBounds.x = displayLocation.x; containerBounds.y = displayLocation.y; if (containerBounds.contains(Display.getCurrent() .getCursorLocation())) { break; } toolTip.hide(); break; } } }; hookRecursively(exitControl, exitListener); } private void initializeColors() { IThemeManager themeManager = PlatformUI.getWorkbench() .getThemeManager(); if (colorWhite == null) { ColorRegistry colorRegistry = JFaceResources.getColorRegistry(); if (!colorRegistry.hasValueFor(COLOR_WHITE)) { colorRegistry.put(COLOR_WHITE, new RGB(255, 255, 255)); } colorWhite = colorRegistry.get(COLOR_WHITE); } if (colorDisabled == null) { ColorRegistry colorRegistry = JFaceResources.getColorRegistry(); if (!colorRegistry.hasValueFor(COLOR_DARK_GRAY)) { colorRegistry.put(COLOR_DARK_GRAY, new RGB(0x69, 0x69, 0x69)); } colorDisabled = colorRegistry.get(COLOR_DARK_GRAY); } if (colorCategoryGradientStart == null) { colorCategoryGradientStart = themeManager.getCurrentTheme() .getColorRegistry().get( CommonThemes.COLOR_CATEGORY_GRADIENT_START); colorCategoryGradientEnd = themeManager.getCurrentTheme() .getColorRegistry().get( CommonThemes.COLOR_CATEGORY_GRADIENT_END); } } private void initializeCursors() { if (handCursor == null) { handCursor = new Cursor(getShell().getDisplay(), SWT.CURSOR_HAND); disposables.add(handCursor); } } private void initializeFonts() { // create a level-2 heading font if (h2Font == null) { Font baseFont = JFaceResources.getDialogFont(); FontData[] fontData = baseFont.getFontData(); for (FontData data : fontData) { data.setStyle(data.getStyle() | SWT.BOLD); data.height = data.height * 1.25f; } h2Font = new Font(Display.getCurrent(), fontData); disposables.add(h2Font); } // create a level-1 heading font if (h1Font == null) { Font baseFont = JFaceResources.getDialogFont(); FontData[] fontData = baseFont.getFontData(); for (FontData data : fontData) { data.setStyle(data.getStyle() | SWT.BOLD); data.height = data.height * 1.35f; } h1Font = new Font(Display.getCurrent(), fontData); disposables.add(h1Font); } } private void initializeImages() { if (infoImage == null) { infoImage = DiscoveryImages.MESSAGE_INFO.createImage(); disposables.add(infoImage); } } public boolean isComplete() { return complete; } /** * indicate if there is nothing to display in the UI, given the current * state of {@link DiscoveryWizard#isVisible(ConnectorDescriptorKind) * filters}. */ private boolean isEmpty(DiscoveryDefinition discovery) { for (Category category : discovery.getCategories()) { if (!isEmpty(category)) { return false; } } return true; } /** * indicate if the category has nothing to display in the UI, given the * current state of * {@link DiscoveryWizard#isVisible(ConnectorDescriptorKind) filters}. */ private boolean isEmpty(Category category) { if (category.getComponents().isEmpty()) { return true; } for (InstallableComponent descriptor : category.getComponents()) { if (!isFiltered(descriptor)) { return false; } } return true; } private boolean isFiltered(InstallableComponent descriptor) { if (!descriptor.isVisible()) return true; boolean kindFiltered = false; for (Group kind : descriptor.getGroups()) { if (!isVisible(kind)) { kindFiltered = true; break; } } if (kindFiltered) { return true; } if (descriptor.isInstalled()) { // always filter installed features per bug 275777 return true; } if (filterPattern != null) { if (!(filterMatches(descriptor.getName()) || filterMatches(descriptor.getDescription()) || filterMatches(descriptor.getProvider()) || filterMatches(descriptor .getLicense()))) { return true; } } return false; } /** * indicate if the connector descriptor filters should be shown in the UI. * Changing this setting only has an effect before the UI is presented. */ public boolean isShowConnectorDescriptorKindFilter() { return showConnectorDescriptorKindFilter; } /** * indicate if a text field should be provided to allow the user to filter * connector descriptors */ public boolean isShowConnectorDescriptorTextFilter() { return showConnectorDescriptorTextFilter; } /** * indicate if the given kind of connector is currently visible in the * wizard * * @see #setVisibility(ConnectorDescriptorKind, boolean) */ public boolean isVisible(Group kind) { if (kind == null) { throw new IllegalArgumentException(); } return connectorDescriptorKindToVisibility.get(kind); } private void modifySelection(final InstallableComponent connector, boolean selected) { modifySelectionInternal(connector, selected); updateState(); } private void modifySelectionInternal(final InstallableComponent connector, boolean selected) { Iterator<Message> it = connector.getMessages().iterator(); while (selected && it.hasNext()) { Message message = it.next(); if (message.getIcon() == Severity.WARNING) { MessageDialog.openWarning(getShell(), message.getTitle(), message.getMessage()); } else if (message.getIcon() == Severity.INFO) { MessageDialog.openInformation(getShell(), message.getTitle(), message.getMessage()); } } connector.setSelected(selected); if (selected) { installableConnectors.add(connector); } else { installableConnectors.remove(connector); } } public void removeSelectionChangedListener( ISelectionChangedListener listener) { selectionProvider.removeSelectionChangedListener(listener); } public void setComplete(boolean complete) { this.complete = complete; } protected void setControl(Control control) { this.control = control; } public void setSelection(IStructuredSelection selection) { Set<InstallableComponent> selected = new HashSet<InstallableComponent>(); for (Object descriptor : selection.toArray()) { if (descriptor instanceof InstallableComponent) { selected.add((InstallableComponent) descriptor); } } for (InstallableComponent connector : allConnectors) { modifySelectionInternal(connector, selected.contains(connector)); } updateState(); } /** * indicate if the connector descriptor filters should be shown in the UI. * Changing this setting only has an effect before the UI is presented. */ public void setShowConnectorDescriptorKindFilter( boolean showConnectorDescriptorKindFilter) { this.showConnectorDescriptorKindFilter = showConnectorDescriptorKindFilter; } /** * indicate if a text field should be provided to allow the user to filter * connector descriptors */ public void setShowConnectorDescriptorTextFilter( boolean showConnectorDescriptorTextFilter) { this.showConnectorDescriptorTextFilter = showConnectorDescriptorTextFilter; } public void setVerifyUpdateSiteAvailability( boolean verifyUpdateSiteAvailability) { this.verifyUpdateSiteAvailability = verifyUpdateSiteAvailability; } /** * configure the page to show or hide connector descriptors of the given * kind * * @see #connectorDescriptorKindVisibilityUpdated() */ public void setVisibility(Group kind, boolean visible) { if (kind == null) { throw new IllegalArgumentException(); } connectorDescriptorKindToVisibility.put(kind, visible); } public void updateDiscovery() { boolean wasCancelled = false; try { context.run(true, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { DiscoveryViewer.this.provider.update(monitor); if (monitor.isCanceled()) { throw new InterruptedException(); } } }); } catch (InvocationTargetException e) { Activator.logHelper.error(Messages.ConnectorDiscoveryWizardMainPage_unexpectedException, e); } catch (InterruptedException e) { // cancelled by user so nothing to do here. wasCancelled = true; } if (provider.getDiscovery() != null) { discoveryUpdated(wasCancelled); if (verifyUpdateSiteAvailability && !allConnectors.isEmpty()) { try { context.run(true, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { AvailabilityUpdater availability = new AvailabilityUpdater( provider.getDiscovery()); availability.update(monitor); } }); } catch (InvocationTargetException e) { Activator.logHelper.error(Messages.ConnectorDiscoveryWizardMainPage_unexpectedException, e); } catch (InterruptedException e) { // cancelled by user so nothing to do here. wasCancelled = true; } } // shouldn't be necessary but for some // reason checkboxes don't // regain their enabled state createBodyContents(); } // help UI tests body.setData("discoveryComplete", "true"); //$NON-NLS-1$//$NON-NLS-2$ } private void updateState() { setComplete(!installableConnectors.isEmpty()); selectionProvider.setSelection(new StructuredSelection( getInstallableConnectors())); } }