| /******************************************************************************* |
| * Copyright (c) 2017 Obeo. |
| * 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: |
| * Obeo - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.sirius.ui.tools.internal.viewpoint; |
| |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.FileLocator; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; |
| import org.eclipse.jface.layout.GridLayoutFactory; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.viewers.ArrayContentProvider; |
| import org.eclipse.jface.viewers.CheckboxTableViewer; |
| import org.eclipse.jface.viewers.DecorationOverlayIcon; |
| import org.eclipse.jface.viewers.IDecoration; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITableLabelProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.TableLayout; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerComparator; |
| import org.eclipse.sirius.business.api.componentization.ViewpointRegistry; |
| import org.eclipse.sirius.business.api.query.IdentifiedElementQuery; |
| import org.eclipse.sirius.business.api.query.ViewpointQuery; |
| import org.eclipse.sirius.business.api.session.Session; |
| import org.eclipse.sirius.common.tools.api.util.StringUtil; |
| import org.eclipse.sirius.ui.tools.api.views.ViewHelper; |
| import org.eclipse.sirius.viewpoint.description.Viewpoint; |
| import org.eclipse.sirius.viewpoint.provider.Messages; |
| import org.eclipse.sirius.viewpoint.provider.SiriusEditPlugin; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTError; |
| import org.eclipse.swt.browser.Browser; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.FontData; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.Text; |
| import org.osgi.framework.Bundle; |
| |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.Collections2; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * A graphic component providing a {@link CheckboxTableViewer} with a checkbox for each viewpoint activable/deactivable |
| * regarding the given session. Also provides a browser allowing to see viewpoint description when one has focus. |
| * |
| * @author <a href="mailto:pierre.guilet@obeo.fr">Pierre Guilet</a> |
| * |
| */ |
| public class ViewpointsSelectionGraphicalHandler { |
| /** |
| * The layout of the main composite of this graphic component. |
| */ |
| private GridLayout rootGridLayout; |
| |
| /** The browser for documentation */ |
| private Browser browser; |
| |
| /** |
| * The composite enclosing all graphical parts of this component. |
| */ |
| private Composite rootComposite; |
| |
| /** |
| * The grid data for the browser component displaying viewpoint information. |
| */ |
| private GridData browserGridData; |
| |
| /** |
| * The viewer containing a checkbox for each viewpoint registered in the current runtime. |
| */ |
| private CheckboxTableViewer viewer; |
| |
| /** |
| * The layout data of the root component. |
| */ |
| private GridData rootLayoutData; |
| |
| private GridData viewerGridData; |
| |
| /** |
| * LayoutData of the error message top part of the browser component. |
| */ |
| private GridData browserErrorMessageLayoutData; |
| |
| /** |
| * The Text containing error message in the top part of the browser component. |
| */ |
| private Text browserErrorMessageText; |
| |
| /** |
| * The composite containing the text containing error message in the top part of the browser component. |
| */ |
| private Composite browserErrorMessageComposite; |
| |
| /** |
| * The root composite of the browser component. |
| */ |
| private Composite browserRootComposite; |
| |
| /** |
| * Return the composite enclosing all graphical parts of this component. |
| * |
| * @return the composite enclosing all graphical parts of this component. |
| */ |
| public Composite getRootComposite() { |
| return rootComposite; |
| } |
| |
| /** |
| * Return the browser providing descriptions for viewpoints taking focus. |
| * |
| * @return the browser providing descriptions for viewpoints taking focus. |
| */ |
| public Browser getBrowser() { |
| return browser; |
| } |
| |
| /** |
| * Returns the root composite of the browser component. |
| * |
| * @return the root composite of the browser component. |
| */ |
| public Composite getBrowserRootComposite() { |
| return browserRootComposite; |
| } |
| |
| /** |
| * Return all registered viewpoints that define editors for metamodel of loaded session's semantic models. |
| * |
| * @param session |
| * the session from which we retrieve available viewpoints. |
| * @return all registered viewpoints that define editors for metamodel of loaded session's semantic models. |
| */ |
| public Collection<Viewpoint> getAvailableViewpoints(Session session) { |
| ViewpointRegistry registry = ViewpointRegistry.getInstance(); |
| |
| return Collections2.filter(registry.getViewpoints(), new Predicate<Viewpoint>() { |
| |
| @Override |
| public boolean apply(Viewpoint viewpoint) { |
| for (final String ext : computeSemanticFileExtensions(session)) { |
| if (new ViewpointQuery(viewpoint).handlesSemanticModelExtension(ext)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| }); |
| } |
| |
| /** |
| * compute the semantic file extensions to restrict the choice of viewpoint based on the session. |
| * |
| * @param theSession |
| * the session |
| * @return a collection of file extension |
| */ |
| public Collection<String> computeSemanticFileExtensions(Session theSession) { |
| final Collection<String> extensions = new HashSet<String>(); |
| for (final Resource resource : theSession.getSemanticResources()) { |
| if (resource != null && resource.getURI() != null) { |
| final String currentFileExtension = resource.getURI().fileExtension(); |
| if (currentFileExtension != null) { |
| extensions.add(currentFileExtension); |
| } |
| } |
| } |
| return extensions; |
| } |
| |
| /** |
| * Initialize graphic components. |
| * |
| * @param parent |
| * the composite from which we attach this graphic component. |
| * @param makeColumnsEqual |
| * If true makes the two columns of the main composite equals at layout level . If false makes the two |
| * columns not equals at layout level. Refresh the layout if a modification is done. |
| */ |
| public void createControl(final Composite parent, boolean makeColumnsEqual) { |
| rootComposite = new Composite(parent, SWT.NONE); |
| rootGridLayout = GridLayoutFactory.swtDefaults().numColumns(2).equalWidth(makeColumnsEqual).create(); |
| rootComposite.setLayout(rootGridLayout); |
| rootLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true); |
| rootComposite.setLayoutData(rootLayoutData); |
| |
| createTableViewer(rootComposite); |
| |
| createBrowser(rootComposite); |
| setBrowserInput(null); |
| |
| } |
| |
| /** |
| * Initialize the graphic component that is a browser allowing to see a description for each viewpoint that has the |
| * focus. |
| * |
| * @param parent |
| * the parent composite to be attached to. |
| * @return the newly created {@link Browser}. |
| */ |
| public Browser createBrowser(final Composite parent) { |
| |
| browserRootComposite = new Composite(parent, SWT.BORDER); |
| browserRootComposite.setLayout(GridLayoutFactory.fillDefaults().create()); |
| browserRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| |
| browserErrorMessageComposite = new Composite(browserRootComposite, SWT.None); |
| browserErrorMessageComposite.setLayout(GridLayoutFactory.fillDefaults().create()); |
| browserErrorMessageLayoutData = new GridData(SWT.FILL, SWT.FILL, true, false); |
| browserErrorMessageComposite.setLayoutData(browserErrorMessageLayoutData); |
| browserErrorMessageLayoutData.exclude = true; |
| |
| Composite browserComposite = new Composite(browserRootComposite, SWT.None); |
| browserComposite.setLayout(GridLayoutFactory.fillDefaults().create()); |
| browserComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| |
| browserErrorMessageText = new Text(browserErrorMessageComposite, SWT.MULTI | SWT.WRAP); |
| browserErrorMessageText.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false)); |
| browserErrorMessageText.setText(""); //$NON-NLS-1$ |
| browserErrorMessageText.setForeground(browserRootComposite.getDisplay().getSystemColor(SWT.COLOR_RED)); |
| try { |
| Browser aBrowser = new Browser(browserComposite, SWT.FILL); |
| browserGridData = new GridData(SWT.FILL, SWT.FILL, true, true); |
| // necessary to avoid bad interaction with expandable toolkit sections |
| browserGridData.widthHint = 0; |
| browserGridData.heightHint = 0; |
| aBrowser.setLayoutData(browserGridData); |
| this.browser = aBrowser; |
| return aBrowser; |
| } catch (SWTError error) { |
| /* |
| * the browser could not be created, do not display further information |
| */ |
| return null; |
| } |
| |
| } |
| |
| /** |
| * Set an error message above the viewpoint browser. |
| * |
| * @param errorMessage |
| * the error message to set. |
| */ |
| public void setBrowserErrorMessageText(String errorMessage) { |
| browserErrorMessageLayoutData.exclude = false; |
| browserErrorMessageText.setText(errorMessage); |
| browserErrorMessageComposite.setVisible(true); |
| browserRootComposite.layout(true, true); |
| } |
| |
| /** |
| * Clear the error message above the viewpoint browser. |
| */ |
| public void clearBrowserErrorMessageText() { |
| browserErrorMessageLayoutData.exclude = true; |
| browserErrorMessageText.setText(""); //$NON-NLS-1$ |
| browserErrorMessageComposite.setVisible(false); |
| browserRootComposite.getParent().layout(true, true); |
| } |
| |
| /** |
| * Sets the minimum width the browser should have. |
| * |
| * @param minwidth |
| * the minimum width to set. |
| */ |
| public void setBrowserMinWidth(int minwidth) { |
| if (browserGridData != null) { |
| browserGridData.minimumWidth = minwidth; |
| } |
| } |
| |
| /** |
| * Returns the viewer containing a checkbox for each viewpoint registered in the current runtime. |
| * |
| * @return the viewer containing a checkbox for each viewpoint registered in the current runtime. |
| */ |
| public CheckboxTableViewer getViewer() { |
| return viewer; |
| } |
| |
| /** |
| * Create the table viewer. |
| * |
| * @param parent |
| * the parent composite. |
| * @return the table viewer. |
| */ |
| private TableViewer createTableViewer(final Composite parent) { |
| final int style = SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER; |
| |
| viewer = CheckboxTableViewer.newCheckList(parent, style); |
| Table table = viewer.getTable(); |
| viewerGridData = new GridData(SWT.FILL, SWT.FILL, false, false); |
| viewer.getControl().setLayoutData(viewerGridData); |
| |
| TableLayout layout = new TableLayout(); |
| table.setLayout(layout); |
| table.setHeaderVisible(false); |
| table.setLinesVisible(false); |
| |
| viewer.setContentProvider(new ArrayContentProvider()); |
| viewer.setLabelProvider(new ViewpointsTableLabelProvider()); |
| viewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| ISelection selection = event.getSelection(); |
| if (selection instanceof IStructuredSelection) { |
| Object firstElement = ((IStructuredSelection) selection).getFirstElement(); |
| if (firstElement instanceof Viewpoint) { |
| setBrowserInput((Viewpoint) firstElement); |
| } |
| } |
| } |
| }); |
| |
| viewer.setComparator(new ViewerComparator() { |
| @Override |
| public int compare(Viewer theViewer, Object e1, Object e2) { |
| final String e1label = new IdentifiedElementQuery((Viewpoint) e1).getLabel(); |
| final String e2label = new IdentifiedElementQuery((Viewpoint) e2).getLabel(); |
| return e1label.compareTo(e2label); |
| } |
| }); |
| return viewer; |
| } |
| |
| /*** |
| * Set the browser input.A jface like browser viewer would have been better. |
| * |
| * @param viewpoint |
| * the viewpoint to document |
| */ |
| public void setBrowserInput(final Viewpoint viewpoint) { |
| |
| /* browser may be null if its creation fail */ |
| if (browser != null) { |
| String content = null; |
| if (containsHTMLDocumentation(viewpoint)) { |
| content = getContentWhenHtml(viewpoint); |
| } else { |
| content = getContentWhenNoHtml(viewpoint); |
| } |
| browser.setText(content); |
| } |
| } |
| |
| /* |
| * The following code (HTML handling ) and methods could move to another class. |
| */ |
| private boolean containsHTMLDocumentation(Viewpoint viewpoint) { |
| if (viewpoint != null) { |
| final String doc = viewpoint.getEndUserDocumentation(); |
| if (!StringUtil.isEmpty(doc)) { |
| return doc.startsWith("<html>"); //$NON-NLS-1$ |
| } |
| } |
| return false; |
| } |
| |
| private String getContentWhenHtml(Viewpoint viewpoint) { |
| |
| final String document = viewpoint.getEndUserDocumentation(); |
| |
| Set<String> urlToRewrite = Sets.newLinkedHashSet(); |
| extractUrlToRewrite(document, urlToRewrite); |
| |
| return rewriteURLs(viewpoint, document, urlToRewrite); |
| } |
| |
| private void extractUrlToRewrite(String document, Set<String> urlToRewrite) { |
| String imgSrcPattern = "img src=\""; //$NON-NLS-1$ |
| int patternStartIndex = document.indexOf(imgSrcPattern); |
| if (patternStartIndex != -1) { |
| int imgSrcStartIndex = patternStartIndex + imgSrcPattern.length(); |
| int imgSrcStopIndex = document.indexOf("\"", imgSrcStartIndex); //$NON-NLS-1$ |
| if (imgSrcStopIndex != -1) { |
| String newToRewrite = document.substring(imgSrcStartIndex, imgSrcStopIndex); |
| urlToRewrite.add(newToRewrite); |
| extractUrlToRewrite(document.substring(imgSrcStopIndex), urlToRewrite); |
| } |
| } |
| } |
| |
| private String rewriteURLs(Viewpoint viewpoint, String document, Set<String> urls) { |
| |
| String newDocument = document; |
| |
| for (final String url : urls) { |
| newDocument = newDocument.replace(url, rewriteURL(viewpoint, url)); |
| } |
| |
| StringBuilder css = new StringBuilder(); |
| appendCss(css); |
| String headClose = "</head>"; //$NON-NLS-1$ |
| newDocument = newDocument.replace(headClose, css.append(headClose)); |
| |
| return newDocument; |
| } |
| |
| private String rewriteURL(Viewpoint viewpoint, String url) { |
| final URI uri = viewpoint.eResource().getURI(); |
| String pluginId = uri.segment(1); |
| |
| String rewrittenURL = ""; //$NON-NLS-1$ |
| |
| if (uri.isPlatformPlugin()) { |
| Bundle bundle = Platform.getBundle(pluginId); |
| URL imageURL = bundle.getEntry(url); |
| rewrittenURL = imageURL != null ? imageURL.toString() : rewrittenURL; |
| if (imageURL != null) { |
| try { |
| URL fileURL = FileLocator.toFileURL(imageURL); |
| rewrittenURL = fileURL.toString(); |
| } catch (IOException e) { |
| // do nothing |
| } |
| } |
| |
| } else { |
| final IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| final IPath path = new Path("/" + pluginId + url); //$NON-NLS-1$ |
| if (workspace.getRoot().exists(path)) { |
| IResource resource = workspace.getRoot().findMember(path); |
| rewrittenURL = resource.getLocation().toFile().toURI().toString(); |
| } |
| } |
| |
| return rewrittenURL; |
| } |
| |
| private String getContentWhenNoHtml(Viewpoint viewpoint) { |
| StringBuilder content = new StringBuilder(); |
| return begin(content).head(content).body(content, viewpoint).end(content); |
| } |
| |
| private ViewpointsSelectionGraphicalHandler begin(StringBuilder content) { |
| content.append("<html>"); //$NON-NLS-1$ |
| return this; |
| } |
| |
| private ViewpointsSelectionGraphicalHandler head(StringBuilder content) { |
| content.append("<head>"); //$NON-NLS-1$ |
| appendCss(content); |
| content.append("</head>"); //$NON-NLS-1$ |
| return this; |
| } |
| |
| private ViewpointsSelectionGraphicalHandler body(StringBuilder content, Viewpoint viewpoint) { |
| content.append("<body>"); //$NON-NLS-1$ |
| |
| if (viewpoint == null) { |
| content.append("<br><br><center><b>").append(Messages.ViewpointsSelectionWizardPage_documentation_title).append("</b></center>"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| final String endUserDocumentation = viewpoint.getEndUserDocumentation(); |
| if (!StringUtil.isEmpty(endUserDocumentation)) { |
| content.append(viewpoint.getEndUserDocumentation()); |
| } else { |
| content.append(Messages.ViewpointsSelectionWizardPage_documentation_none); |
| } |
| } |
| content.append("</body>"); //$NON-NLS-1$ |
| return this; |
| } |
| |
| private StringBuilder appendCss(StringBuilder content) { |
| Font currentFont = JFaceResources.getDialogFont(); |
| FontData data = currentFont.getFontData()[0]; |
| String fontName = data.getName(); |
| int fontHeight = data.getHeight() + 3; |
| content.append("<style type=\"text/css\">"); //$NON-NLS-1$ |
| content.append("body{font-family:" + fontName + ",Arial, sans-serif;}"); //$NON-NLS-1$ //$NON-NLS-2$ |
| content.append("body{font-size:" + fontHeight + "px;}"); //$NON-NLS-1$ //$NON-NLS-2$ |
| content.append("</style>"); //$NON-NLS-1$ |
| return content; |
| } |
| |
| private String end(StringBuilder content) { |
| content.append("</html>"); //$NON-NLS-1$ |
| return content.toString(); |
| } |
| |
| private class ViewpointsTableLabelProvider extends AdapterFactoryLabelProvider implements ITableLabelProvider { |
| |
| ViewpointsTableLabelProvider() { |
| super(ViewHelper.INSTANCE.createAdapterFactory()); |
| } |
| |
| @Override |
| public Image getColumnImage(Object element, int columnIndex) { |
| Image image = null; |
| if (columnIndex == 0) { |
| if (element instanceof Viewpoint) { |
| final Viewpoint vp = (Viewpoint) element; |
| if (vp.getIcon() != null && vp.getIcon().length() > 0) { |
| final ImageDescriptor desc = SiriusEditPlugin.Implementation.findImageDescriptor(vp.getIcon()); |
| if (desc != null) { |
| image = SiriusEditPlugin.getPlugin().getImage(desc); |
| image = getEnhancedImage(image, vp); |
| } |
| } |
| if (image == null) { |
| image = SiriusEditPlugin.getPlugin().getImage(SiriusEditPlugin.getPlugin().getItemImageDescriptor(vp)); |
| image = getEnhancedImage(image, vp); |
| } |
| } else { |
| image = super.getImage(element); |
| } |
| } |
| return image; |
| } |
| |
| private ImageDescriptor getOverlayedDescriptor(final Image baseImage, final String decoratorPath) { |
| final ImageDescriptor decoratorDescriptor = SiriusEditPlugin.Implementation.getBundledImageDescriptor(decoratorPath); |
| return new DecorationOverlayIcon(baseImage, decoratorDescriptor, IDecoration.BOTTOM_LEFT); |
| } |
| |
| private Image getEnhancedImage(final Image image, final Viewpoint viewpoint) { |
| // Add decorator if the viewpoint comes from workspace |
| if (!ViewpointRegistry.getInstance().isFromPlugin(viewpoint) && image != null) { |
| return SiriusEditPlugin.getPlugin().getImage(getOverlayedDescriptor(image, "icons/full/decorator/folder_close.gif")); //$NON-NLS-1$ |
| } |
| return image; |
| } |
| } |
| |
| /** |
| * Sets the height the enclosing root composite of the viewpoints block must have. |
| * |
| * @param height |
| * the height to set. |
| */ |
| public void setHeight(int height) { |
| viewerGridData.grabExcessVerticalSpace = false; |
| viewerGridData.heightHint = height; |
| } |
| } |