[564658] New Feature : Export Diagram Comparison as images. A new feature to export a diagram comparison as images. The image is formed by the two compared diagrams with their phantoms and markers, the two diagrams are separated by a black separator. Bug: 564658 Change-Id: I762359439cfeaf25a4f449d852c8e4ec7c1138f2 Signed-off-by: Glenn Plouhinec <glenn.plouhinec@obeo.fr>
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/META-INF/MANIFEST.MF index efb19b4..f948790 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/META-INF/MANIFEST.MF
@@ -11,16 +11,18 @@ org.eclipse.core.runtime, org.eclipse.core.resources, org.eclipse.ui.ide;bundle-version="3.5.0", - org.eclipse.emf.edit;bundle-version="2.5.0", org.eclipse.emf.edit.ui;bundle-version="2.5.0", org.eclipse.emf.compare;bundle-version="3.2.0", org.eclipse.emf.compare.rcp.ui;bundle-version="4.1.0", org.eclipse.emf.compare.ide.ui;bundle-version="4.1.0", org.eclipse.emf.compare.diagram, org.eclipse.emf.compare.diagram.ide.ui;bundle-version="3.0.0", - org.eclipse.sirius.diagram;bundle-version="3.0.0", - org.eclipse.gmf.runtime.notation;bundle-version="1.8.0", - org.eclipse.gef + org.eclipse.sirius.diagram.ui, + org.eclipse.gmf.runtime.diagram.ui, + org.eclipse.sirius.common, + org.eclipse.gmf.runtime.diagram.ui.render, + org.eclipse.gmf.runtime.draw2d.ui.render.awt, + org.eclipse.sirius.common.ui Import-Package: com.google.common.annotations;version="[27.0.0,28.0.0)", com.google.common.base;version="[27.0.0,28.0.0)", com.google.common.cache;version="[27.0.0,28.0.0)",
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/build.properties b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/build.properties index 6b5eea4..117a97f 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/build.properties +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/build.properties
@@ -4,5 +4,8 @@ .,\ about.html,\ plugin.properties,\ - plugin.xml -src.includes = about.html \ No newline at end of file + plugin.xml,\ + icons/ +src.includes = about.html,\ + icons/ +
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/icons/screenshot.gif b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/icons/screenshot.gif new file mode 100644 index 0000000..e3ebf21 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/icons/screenshot.gif Binary files differ
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/CompareDiagramIDEUISiriusPlugin.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/CompareDiagramIDEUISiriusPlugin.java index 4728aed..394e78e 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/CompareDiagramIDEUISiriusPlugin.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/CompareDiagramIDEUISiriusPlugin.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Obeo. + * Copyright (c) 2015, 2020 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 @@ -10,6 +10,9 @@ *******************************************************************************/ package org.eclipse.emf.compare.diagram.ide.ui.sirius; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; @@ -64,4 +67,25 @@ return plugin; } + /** + * Returns a plugin image descriptor. + * + * @param imagePath + * plugin relative path to the image + * @return ImageDescriptor image descriptor. + */ + public static ImageDescriptor getImageDescriptor(String imagePath) { + return imageDescriptorFromPlugin(PLUGIN_ID, imagePath); + } + + /** + * Log an {@link Exception} in the {@link #getLog() current logger}. + * + * @param e + * the exception to be logged. + */ + public void log(Throwable e) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage(), e)); + } + }
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/SiriusContentMergeViewer.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/SiriusContentMergeViewer.java index 0d914ea..0fecee1 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/SiriusContentMergeViewer.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/SiriusContentMergeViewer.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 Obeo. + * Copyright (c) 2018, 2020 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 @@ -16,8 +16,11 @@ import org.eclipse.emf.compare.diagram.ide.ui.internal.accessor.IDiagramNodeAccessor; import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer; +import org.eclipse.emf.compare.diagram.ide.ui.sirius.internal.tools.actions.SiriusComparisonAsImageFileAction; import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration; import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.ToolBarManager; import org.eclipse.sirius.diagram.DDiagram; import org.eclipse.sirius.diagram.business.api.componentization.DiagramDescriptionMappingsManager; import org.eclipse.sirius.diagram.business.api.componentization.DiagramDescriptionMappingsRegistry; @@ -26,15 +29,34 @@ import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.widgets.Composite; +/** + * Specialized {@link org.eclipse.compare.contentmergeviewer.ContentMergeViewer} used to display Sirius + * diagram comparison. + * + * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> + */ public class SiriusContentMergeViewer extends DiagramContentMergeViewer { private Set<DiagramMappingsManager> siriusMappingManagers = new LinkedHashSet<>(); private Set<DiagramDescriptionMappingsManager> siriusDescriptionMappingManagers = new LinkedHashSet<>(); + /** + * Default constructor. + * + * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer#DiagramContentMergeViewer(Composite, + * EMFCompareConfiguration) + * @param parent + * the parent composite to build the UI in. + * @param config + * the {@link EMFCompareConfiguration}. + */ public SiriusContentMergeViewer(Composite parent, EMFCompareConfiguration config) { super(parent, config); } + /** + * {@inheritDoc} + */ @Override protected void updateContent(Object ancestor, Object left, Object right) { super.updateContent(ancestor, left, right); @@ -43,6 +65,9 @@ getSiriusDiagram(right).ifPresent(this::storeManagers); } + /** + * {@inheritDoc} + */ @Override protected void handleDispose(DisposeEvent event) { super.handleDispose(event); @@ -72,4 +97,14 @@ siriusDescriptionMappingManagers.add(DiagramDescriptionMappingsRegistry.INSTANCE .getDiagramDescriptionMappingsManager(null, diagram.getDescription())); } + + /** + * {@inheritDoc} + */ + @Override + protected void createToolItems(ToolBarManager toolBarManager) { + super.createToolItems(toolBarManager); + SiriusComparisonAsImageFileAction saveAsImageFileAction = new SiriusComparisonAsImageFileAction(this); + toolBarManager.insert(0, new ActionContributionItem(saveAsImageFileAction)); + } }
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/actions/ExportComparisonAction.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/actions/ExportComparisonAction.java new file mode 100644 index 0000000..3b15f41 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/actions/ExportComparisonAction.java
@@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2020 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.emf.compare.diagram.ide.ui.sirius.internal.tools.actions; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator; +import org.eclipse.emf.compare.diagram.ide.ui.sirius.internal.tools.image.EMFCompareCopyToImageUtil; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.image.ImageFileFormat; +import org.eclipse.ui.actions.WorkspaceModifyOperation; + +/** + * Export action for diagram comparison. + * + * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a> + */ +@SuppressWarnings("restriction") +public class ExportComparisonAction extends WorkspaceModifyOperation { + + /** + * The diagram to be copied from the left side of the comparison in the ContentMergeViewer. + */ + private DiagramEditPart leftDiagramEditPart; + + /** + * The diagram to be copied from the right side of the comparison in the ContentMergeViewer. + */ + private DiagramEditPart rightDiagramEditPart; + + /** + * Decorators representing Phantoms and Markers in the ContentMergeViewer. + */ + private Collection<AbstractDecorator> decorators; + + /** + * The destination folder chosen in the dialog. + */ + private IPath outputPath; + + /** + * The image format chosen in the dialog. + */ + private ImageFileFormat imageFormat; + + /** + * Constructor used to export leftDiagramEditPart and rightDiagramEditPart as image. + * + * @param path + * the folder in which store the images, result of the export. + * @param format + * the format of the image, result of the export. + * @param leftDEP + * The left {@link DiagramEditPart} to be copied. + * @param rightDEP + * The right {@link DiagramEditPart} to be copied. + * @param abstractDecorators + * The {@link AbstractDecorator}s of the ContentMergeViewer. + */ + public ExportComparisonAction(IPath path, ImageFileFormat format, DiagramEditPart leftDEP, + DiagramEditPart rightDEP, Collection<AbstractDecorator> abstractDecorators) { + outputPath = path; + imageFormat = format; + leftDiagramEditPart = leftDEP; + rightDiagramEditPart = rightDEP; + decorators = abstractDecorators; + } + + /** + * Overridden to do the export work. {@inheritDoc} + */ + @Override + protected void execute(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + try { + createImageFiles(monitor); + } catch (final OutOfMemoryError | CoreException e) { + // Catching OOM as this could be thrown if the image to export requires too large a buffer + throw new InvocationTargetException(e, e.getLocalizedMessage()); + } finally { + monitor.done(); + if (monitor.isCanceled()) { + throw new InterruptedException("The operation was cancelled"); + } + } + } + + /** + * Create the image files. + * + * @param monitor + * the progress monitor + * @throws CoreException + * if one or several images creation failed. The Exception contains a Status with the causes + * of this Exception. + */ + protected void createImageFiles(final IProgressMonitor monitor) throws CoreException { + EMFCompareCopyToImageUtil tool = new EMFCompareCopyToImageUtil(leftDiagramEditPart, + rightDiagramEditPart, decorators); + tool.copyAllToImage(outputPath, imageFormat, monitor); + } +}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/actions/SiriusComparisonAsImageFileAction.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/actions/SiriusComparisonAsImageFileAction.java new file mode 100644 index 0000000..bbf4c6a --- /dev/null +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/actions/SiriusComparisonAsImageFileAction.java
@@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2020 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.emf.compare.diagram.ide.ui.sirius.internal.tools.actions; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramContentMergeViewer; +import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.DiagramMergeViewer; +import org.eclipse.emf.compare.diagram.ide.ui.sirius.CompareDiagramIDEUISiriusPlugin; +import org.eclipse.emf.compare.diagram.ide.ui.sirius.internal.tools.dialog.ExportComparisonDialog; +import org.eclipse.gef.RootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.window.Window; +import org.eclipse.sirius.ui.tools.api.actions.export.SizeTooLargeException; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * This class allows to run the comparison export as image. + * + * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a> + */ +@SuppressWarnings("restriction") +public class SiriusComparisonAsImageFileAction extends Action { + + /** + * The {@link DiagramContentMergeViewer} which contains the left, right and ancestor + * {@link DiagramMergeViewer} once initialized. + */ + private DiagramContentMergeViewer contentMergeViewer; + + /** + * The left {@link DiagramEditPart} to export as image. + */ + private DiagramEditPart leftDiagramEditPart; + + /** + * The right {@link DiagramEditPart} to export as image. + */ + private DiagramEditPart rightDiagramEditPart; + + /** + * Constructor. + * + * @param mergeViewer + * the {@link DiagramContentMergeViewer}. + */ + public SiriusComparisonAsImageFileAction(DiagramContentMergeViewer mergeViewer) { + super("Export diagram comparison as image", + CompareDiagramIDEUISiriusPlugin.getImageDescriptor("icons/screenshot.gif")); //$NON-NLS-1$ + contentMergeViewer = mergeViewer; + } + + @Override + public void run() { + setEditParts(); + exportRepresentation(); + } + + /** + * Used to initialize leftDiagramEditPart and rightDiagramEditPart once the + * {@link DiagramContentMergeViewer} has been set. + */ + private void setEditParts() { + RootEditPart leftRootEP = contentMergeViewer.getLeftMergeViewer().getGraphicalViewer() + .getRootEditPart(); + RootEditPart rightRootEP = contentMergeViewer.getRightMergeViewer().getGraphicalViewer() + .getRootEditPart(); + if (leftRootEP instanceof DiagramRootEditPart && rightRootEP instanceof DiagramRootEditPart) { + DiagramRootEditPart leftRootEditPart = (DiagramRootEditPart)leftRootEP; + DiagramRootEditPart rightRootEditPart = (DiagramRootEditPart)rightRootEP; + if (leftRootEditPart.getContents() instanceof DiagramEditPart + && rightRootEditPart.getContents() instanceof DiagramEditPart) { + leftDiagramEditPart = (DiagramEditPart)leftRootEditPart.getContents(); + rightDiagramEditPart = (DiagramEditPart)rightRootEditPart.getContents(); + } + } + } + + /** + * Display the export path and file format dialog and then export the representations. + */ + protected void exportRepresentation() { + + final Shell shell = Display.getCurrent().getActiveShell(); + ExportComparisonDialog dialog = new ExportComparisonDialog(shell); + + if (dialog.open() == Window.OK) { + final ExportComparisonAction exportAction = new ExportComparisonAction(dialog.getOutputPath(), + dialog.getImageFormat(), leftDiagramEditPart, rightDiagramEditPart, + contentMergeViewer.getDecoratorsManager().getAllDecorators()); + final ProgressMonitorDialog pmd = new ProgressMonitorDialog(shell); + + try { + pmd.run(false, false, exportAction); + } catch (final InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof OutOfMemoryError) { + MessageDialog.openError(shell, "Not enough memory available to create image files", + cause.getMessage()); + } else if (cause instanceof SizeTooLargeException) { + MessageDialog.openError(shell, "Image export impossible", cause.getMessage()); + CompareDiagramIDEUISiriusPlugin.getDefault().log(cause); + } else { + MessageDialog.openError(shell, "Error", cause.getMessage()); + } + } catch (final InterruptedException e) { + MessageDialog.openInformation(shell, "Error", e.getMessage()); + } finally { + pmd.close(); + } + + } else { + dialog.close(); + } + } +}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/dialog/ExportComparisonDialog.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/dialog/ExportComparisonDialog.java new file mode 100644 index 0000000..8a0b5d0 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/dialog/ExportComparisonDialog.java
@@ -0,0 +1,599 @@ +/******************************************************************************* + * Copyright (c) 2020 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.emf.compare.diagram.ide.ui.sirius.internal.tools.dialog; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.emf.compare.diagram.ide.ui.sirius.CompareDiagramIDEUISiriusPlugin; +import org.eclipse.gmf.runtime.diagram.ui.image.ImageFileFormat; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.sirius.common.ui.tools.api.util.SWTUtil; +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.graphics.GC; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** + * Dialog used to export a graphical comparison as image. + * + * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a> + */ +public class ExportComparisonDialog extends Dialog { + + /** + * The id for the persistent settings for this dialog. + */ + public static final String DIALOG_SETTINGS_ID = "ExportComparisonAsImagesDialog"; //$NON-NLS-1$ + + /** + * Default extension image. + */ + protected static final ImageFileFormat DEFAULT_VALUE = ImageFileFormat.JPG; + + /** + * The list of values for this enumerated type. + */ + protected static final ImageFileFormat[] SAFE_VALUES = {DEFAULT_VALUE, ImageFileFormat.PNG, + ImageFileFormat.BMP, ImageFileFormat.GIF, }; + + /** + * The empty string. + */ + protected static final String EMPTY_STRING = ""; //$NON-NLS-1$ + + /** + * The image format label text. + */ + protected static final String IMAGE_FORMAT_LABEL = "&Image Format"; + + /** + * The browse button text. + */ + protected static final String BROWSE_LABEL = "&Browse..."; + + /** + * The id for the persistent image format setting for this dialog. + */ + protected static final String DIALOG_SETTINGS_IMAGE_FORMAT = "ExportComparisonAsImageDialog.imageFormat"; //$NON-NLS-1$ + + /** + * An error access message. + */ + protected static final String ACCES_ERROR_MESSAGE = "Access to path is impossible"; + + /** + * An error folder not exist message. + */ + protected static final String FOLDER_NOT_EXIST_MESSAGE = "Folder does not exist"; + + /** + * The id for the persistent folder setting for this dialog. + */ + private static final String DIALOG_SETTINGS_FOLDER = "ExportComparisonAsImageDialog.folder"; //$NON-NLS-1$ + + /** + * The directory dialog message. + */ + private static final String DIRECTORY_DIALOG_MESSAGE = "Select the destination folder"; + + /** + * The directory dialog text. + */ + private static final String DIRECTORY_DIALOG_TEXT = DIRECTORY_DIALOG_MESSAGE; + + /** + * Popup file field label. + */ + private static final String TO_DIRECTORY = "To directory"; + + /** + * The dialog window title. + */ + private static final String DIALOG_TITLE = "Export comparison as image file"; + + /** + * Combo length history path. + */ + private static final int COMBO_HISTORY_LENGTH = 5; + + /** + * Height of the browse button. + */ + private static final int HEIGHT_BROWSE_BUTTON = 14; + + /** + * Width of the folder combo field. + */ + private static final int WIDTH_FOLDER_TEXT = 350; + + /** + * Width of the message field. + */ + private static final int WIDTH_MESSAGE = 300; + + /** + * The folder combo field. + */ + protected Combo folderText; + + /** + * The extension image combo field. + */ + protected Combo imageFormatCombo; + + /** + * The image format selected in the image format pulldown field. + */ + protected ImageFileFormat imageFormat; + + /** + * The text entered into the folder text field. + */ + protected String folder; + + /** + * The message image field, displays the error (X) icon when the file name or folder is invalid. + */ + private Label messageImageLabel; + + /** + * The message field, displays an error message when the file name or folder is invalid. + */ + private Label messageLabel; + + /** + * Creates an instance of the copy to image dialog. + * + * @param parentShell + * the parent shell + */ + public ExportComparisonDialog(Shell parentShell) { + super(parentShell); + initDialogSettings(); + } + + /** + * Resolve the selected image format to an enumerated type. + * + * @param ordinal + * the selected format in the pulldown + * @return the image format enumerated type + */ + public static ImageFileFormat resolveImageFormat(final int ordinal) { + return SAFE_VALUES[ordinal]; + } + + /** + * Resolve the selected image format to an enumerated type. + * + * @param imageFormat + * the selected format. + * @return the image format enumerated type + */ + public static ImageFileFormat resolveImageFormat(final String imageFormat) { + for (ImageFileFormat element : SAFE_VALUES) { + if (element.getName().toLowerCase().equals(imageFormat.toLowerCase())) { + return element; + } + } + return getDefaultImageFormat(); + } + + /** + * Retrieves the default image format. + * + * @return the default image format. + */ + public static ImageFileFormat getDefaultImageFormat() { + return ImageFileFormat.JPG; + } + + /** + * Returns height and width data in a GridData for the button that was passed in. You can call + * button.setLayoutData with the returned data. + * + * @param button + * which has already been made. We'll be making the GridData for this button, so be sure that + * the text has already been set. + * @return GridData for this button with the suggested height and width + */ + public static GridData makeButtonData(Button button) { + GC gc = new GC(button); + gc.setFont(button.getFont()); + + GridData data = new GridData(); + data.heightHint = Dialog.convertVerticalDLUsToPixels(gc.getFontMetrics(), HEIGHT_BROWSE_BUTTON); + data.widthHint = Math.max( + Dialog.convertHorizontalDLUsToPixels(gc.getFontMetrics(), IDialogConstants.BUTTON_WIDTH), + button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x); + + gc.dispose(); + + return data; + } + + /** + * Returns the output path. + * + * @return folder + */ + public IPath getOutputPath() { + return new Path(folder); + } + + /** + * Returns the destination image file format selected by the user. + * + * @return the selected image file format. + */ + public ImageFileFormat getImageFormat() { + return imageFormat; + } + + /** + * Retrieves the persistent settings for this dialog. + * + * @return the persistent settings for this dialog. + */ + protected IDialogSettings getDialogSettings() { + IDialogSettings settings = CompareDiagramIDEUISiriusPlugin.getDefault().getDialogSettings(); + settings = settings.getSection(DIALOG_SETTINGS_ID); + if (settings == null) { + settings = CompareDiagramIDEUISiriusPlugin.getDefault().getDialogSettings() + .addNewSection(DIALOG_SETTINGS_ID); + } + return settings; + } + + /** + * Initialize the settings for this dialog. + */ + protected void initDialogSettings() { + final IDialogSettings dialogSettings = getDialogSettings(); + folder = "/"; //$NON-NLS-1$ + String workspace = ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString(); + if (workspace != null && !workspace.equals(EMPTY_STRING)) { + folder = workspace; + } + + final String persistentFolder = dialogSettings.get(DIALOG_SETTINGS_FOLDER); + if (persistentFolder != null) { + folder = persistentFolder; + } + + final String persistentImageFormat = dialogSettings.get(DIALOG_SETTINGS_IMAGE_FORMAT); + if (persistentImageFormat == null) { + imageFormat = getDefaultImageFormat(); + } else { + imageFormat = resolveImageFormat(persistentImageFormat); + } + } + + /** + * Retrieves the persistent settings for this dialog. + */ + protected void saveDialogSettings() { + final IDialogSettings dialogSettings = getDialogSettings(); + if (dialogSettings != null) { + String[] directoryNames = dialogSettings.getArray(DIALOG_SETTINGS_FOLDER); + if (directoryNames == null || directoryNames.length == 0) { + directoryNames = new String[1]; + directoryNames[0] = folder; + } + directoryNames = addToHistory(directoryNames, folder); + dialogSettings.put(DIALOG_SETTINGS_FOLDER, directoryNames); + dialogSettings.put(DIALOG_SETTINGS_IMAGE_FORMAT, imageFormat.getName()); + } + } + + @Override + protected void okPressed() { + imageFormat = resolveImageFormat(imageFormatCombo.getSelectionIndex()); + super.okPressed(); + saveDialogSettings(); + } + + /** + * Handle a browse button pressed selection. + */ + protected void handleBrowseButtonPressed() { + final DirectoryDialog dialog = new DirectoryDialog(Display.getCurrent().getActiveShell()); + dialog.setMessage(DIRECTORY_DIALOG_MESSAGE); + dialog.setText(DIRECTORY_DIALOG_TEXT); + + final String dirName = folderText.getText(); + if (!dirName.equals(EMPTY_STRING)) { + final File path = new File(dirName); + if (path.exists()) { + dialog.setFilterPath(new Path(dirName).toOSString()); + } + } + + final String selectedDirectory = dialog.open(); + if (selectedDirectory != null) { + folderText.setText(selectedDirectory); + } + } + + /** + * Validate the folder text field. + */ + protected void validateFolderText() { + + if (folderText.getText().equals(EMPTY_STRING)) { + + setDialogErrorState("Folder cannot be blank"); + + } else { + + IPath path = new Path(EMPTY_STRING); + if (!path.isValidPath(folderText.getText())) { + setDialogErrorState("Folder is not a valid path"); + return; + } + + final File file = new File(folderText.getText()); + final File directory = file.getParentFile(); + + if (file.exists()) { + if (!file.canWrite()) { + setDialogErrorState(ACCES_ERROR_MESSAGE); + return; + } + } else if (directory != null && !directory.canWrite()) { + setDialogErrorState(ACCES_ERROR_MESSAGE); + return; + } + folder = folderText.getText(); + setDialogOKState(); + } + } + + /** + * Set the dialog into error state mode. The error image (x) label and error label are made visible and + * the ok button is disabled. + * + * @param message + * the error message + */ + protected void setDialogErrorState(final String message) { + messageLabel.setText(message); + messageImageLabel.setVisible(true); + messageLabel.setVisible(true); + getButton(IDialogConstants.OK_ID).setEnabled(false); + getButton(IDialogConstants.CANCEL_ID).getShell() + .setDefaultButton(getButton(IDialogConstants.CANCEL_ID)); + } + + /** + * Get the supported image formats from the enumerated type. + * + * @return array of supported image formats. + */ + protected String[] getImageSafeFormatItems() { + final String[] items = new String[SAFE_VALUES.length]; + for (int i = 0; i < SAFE_VALUES.length; i++) { + items[i] = SAFE_VALUES[i].getName(); + } + return items; + } + + @Override + protected Control createContents(Composite parent) { + Control result = super.createContents(parent); + validateFolderText(); + return result; + } + + @Override + protected Control createDialogArea(final Composite parent) { + final Composite composite = (Composite)super.createDialogArea(parent); + createFolderGroup(composite); + createImageFormatGroup(composite); + createMessageGroup(composite); + initListeners(); + return composite; + } + + /** + * Create the folder group in the dialog. + * + * @param parent + * the parent widget + */ + protected void createFolderGroup(final Composite parent) { + final Composite composite = SWTUtil.createCompositeHorizontalFill(parent, 3, false); + SWTUtil.createLabel(composite, TO_DIRECTORY); + + folderText = new Combo(composite, SWT.SINGLE | SWT.BORDER); + restoreWidgetValues(); + folderText.setText(folder); + final GridData gridData = new GridData(GridData.FILL_HORIZONTAL); + gridData.widthHint = WIDTH_FOLDER_TEXT; + folderText.setLayoutData(gridData); + + final Button button = new Button(composite, SWT.PUSH); + button.setText(BROWSE_LABEL); + button.setLayoutData(makeButtonData(button)); + button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent event) { + handleBrowseButtonPressed(); + } + }); + } + + /** + * Create the image format group in the dialog. + * + * @param parent + * the parent widget + */ + protected void createImageFormatGroup(final Composite parent) { + final Composite composite = SWTUtil.createCompositeHorizontalFill(parent, 2, false); + SWTUtil.createLabel(composite, IMAGE_FORMAT_LABEL); + + String[] imageSafeFormatItems = getImageSafeFormatItems(); + + Control imageFormatControl; + if (imageSafeFormatItems != null && imageSafeFormatItems.length > 0) { + + if (imageSafeFormatItems.length == 1) { + imageFormatControl = SWTUtil.createLabel(composite, imageSafeFormatItems[0]); + + } else { + imageFormatCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY); + imageFormatCombo.setItems(imageSafeFormatItems); + imageFormatCombo.setText(imageFormat.getName()); + imageFormatControl = imageFormatCombo; + } + } else { + imageFormatControl = SWTUtil.createLabel(composite, DEFAULT_VALUE.getName()); + } + final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true); + imageFormatControl.setLayoutData(gridData); + + } + + /** + * {@inheritDoc} Configures the shell in preparation for opening this window in it. + * + * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell) + */ + @Override + protected void configureShell(final Shell shell) { + super.configureShell(shell); + shell.setText(DIALOG_TITLE); + } + + /** + * Hook method for restoring widget values to the values that they held last time this wizard was used to + * completion. + */ + protected void restoreWidgetValues() { + IDialogSettings settings = getDialogSettings(); + if (settings != null) { + String[] directoryNames = settings.getArray(DIALOG_SETTINGS_FOLDER); + if (directoryNames == null || directoryNames.length == 0) { + return; + } + folder = directoryNames[0]; + for (String directoryName : directoryNames) { + folderText.add(directoryName); + } + } + } + + /** + * Create the message group in the dialog used to display error messages. + * + * @param parent + * the parent widget + */ + private void createMessageGroup(final Composite parent) { + final Composite composite = SWTUtil.createCompositeHorizontalFill(parent, 2, false); + + messageImageLabel = new Label(composite, SWT.NONE); + messageImageLabel.setImage(JFaceResources.getImage(DLG_IMG_MESSAGE_ERROR)); + messageImageLabel.setVisible(false); + + messageLabel = new Label(composite, SWT.NONE); + final GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_HORIZONTAL); + gridData.widthHint = WIDTH_MESSAGE; + messageLabel.setLayoutData(gridData); + messageLabel.setVisible(false); + } + + /** + * Set the dialog into ok state mode. The error image (x) label and error label and made not visible and + * the ok button is enabled. + */ + private void setDialogOKState() { + messageImageLabel.setVisible(false); + messageLabel.setVisible(false); + getButton(IDialogConstants.OK_ID).setEnabled(true); + getButton(IDialogConstants.OK_ID).getShell().setDefaultButton(getButton(IDialogConstants.OK_ID)); + } + + /** + * Adds an entry to a history, while taking care of duplicate history items and excessively long + * histories. The assumption is made that all histories should be of length + * <code>WizardDataTransferPage.COMBO_HISTORY_LENGTH</code>. + * + * @param history + * the current history + * @param newEntry + * the entry to add to the history + * @return String[] tab of history + */ + protected String[] addToHistory(String[] history, String newEntry) { + java.util.ArrayList<String> l = new java.util.ArrayList<String>(Arrays.asList(history)); + addToHistory(l, newEntry); + String[] r = new String[l.size()]; + l.toArray(r); + return r; + } + + /** + * Adds an entry to a history, while taking care of duplicate history items and excessively long + * histories. The assumption is made that all histories should be of length + * <code>WizardDataTransferPage.COMBO_HISTORY_LENGTH</code>. + * + * @param history + * the current history + * @param newEntry + * the entry to add to the history + */ + protected void addToHistory(List<String> history, String newEntry) { + history.remove(newEntry); + history.add(0, newEntry); + if (history.size() > COMBO_HISTORY_LENGTH) { + history.remove(COMBO_HISTORY_LENGTH); + } + } + + /** + * Initialize the field listeners when all field are created. + */ + protected void initListeners() { + folderText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(final ModifyEvent e) { + validateFolderText(); + } + }); + imageFormatCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent e) { + imageFormat = resolveImageFormat(imageFormatCombo.getSelectionIndex()); + } + }); + } +}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/image/EMFCompareCopyToImageUtil.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/image/EMFCompareCopyToImageUtil.java new file mode 100644 index 0000000..2883db9 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/image/EMFCompareCopyToImageUtil.java
@@ -0,0 +1,300 @@ +/******************************************************************************* + * Copyright (c) 2020 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.emf.compare.diagram.ide.ui.sirius.internal.tools.image; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.imageio.ImageIO; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator; +import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.image.ImageFileFormat; +import org.eclipse.gmf.runtime.diagram.ui.render.clipboard.DiagramGenerator; +import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.image.ImageExporter; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.sirius.common.tools.api.util.FileUtil; +import org.eclipse.sirius.diagram.DDiagram; +import org.eclipse.sirius.diagram.ui.tools.api.part.DiagramEditPartService; + +/** + * This class is used to manage the creation of images from DiagramEditPart, using the Sirius API. + * + * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a> + */ +@SuppressWarnings("restriction") +public class EMFCompareCopyToImageUtil extends DiagramEditPartService { + + /** + * The diagram to be copied from the left side of the comparison in the ContentMergeViewer. + */ + private DiagramEditPart leftDiagramEditPart; + + /** + * The diagram to be copied from the right side of the comparison in the ContentMergeViewer. + */ + private DiagramEditPart rightDiagramEditPart; + + /** + * Decorators representing Phantoms and Markers in the ContentMergeViewer. + */ + private Collection<AbstractDecorator> decorators; + + /** + * Constructor. + * + * @param leftDEP + * The left {@link DiagramEditPart} to be copied. + * @param rightDEP + * The right {@link DiagramEditPart} to be copied. + * @param abstractDecorators + * The {@link AbstractDecorator}s of the ContentMergeViewer. + */ + public EMFCompareCopyToImageUtil(DiagramEditPart leftDEP, DiagramEditPart rightDEP, + Collection<AbstractDecorator> abstractDecorators) { + leftDiagramEditPart = leftDEP; + rightDiagramEditPart = rightDEP; + decorators = abstractDecorators; + } + + /** + * Get the left {@link DiagramEditPart} to be copied. + * + * @return the left {@link DiagramEditPart} to be copied. + */ + public DiagramEditPart getLeftDiagramEditPart() { + return leftDiagramEditPart; + } + + /** + * Get the right {@link DiagramEditPart} to be copied. + * + * @return the right {@link DiagramEditPart} to be copied. + */ + public DiagramEditPart getRightDiagramEditPart() { + return rightDiagramEditPart; + } + + /** + * Get the {@link AbstractDecorator}s of the ContentMergeViewer. + * + * @return the {@link AbstractDecorator}s of the ContentMergeViewer. + */ + public Collection<AbstractDecorator> getDecorators() { + return decorators; + } + + /** + * Only overridden to use {@link EMFCompareDiagramImageGenerator} instead of + * org.eclipse.sirius.diagram.ui.tools.internal.render.SiriusDiagramImageGenerator. + */ + @Override + protected DiagramGenerator getDiagramGenerator(DiagramEditPart diagramEP, ImageFileFormat format) { + return new EMFCompareDiagramImageGenerator(diagramEP, decorators, getSide(diagramEP)); + } + + /** + * Used to find out which side of the MergeViewer the diagram belongs to. + * + * @param diagramEP + * the {@link DiagramEditPart} for which we are looking for the {@link MergeViewerSide}. + * @return the corresponding {@link MergeViewerSide}. + */ + protected MergeViewerSide getSide(DiagramEditPart diagramEP) { + if (diagramEP.equals(leftDiagramEditPart)) { + return MergeViewerSide.LEFT; + } else { + return MergeViewerSide.RIGHT; + } + } + + /** + * Executes the creation of all images. One image is created for the LeftDiagramEditPart, and another one + * for the RightDiagramEditPart, then the two images are joined together to return the image resulting + * from the diagram comparison. + * + * @param destination + * the destination folder chosen in the dialog. + * @param format + * the image format chosen in the dialog. + * @param monitor + * the progress monitor. + * @throws CoreException + * if the export of the image to a file has failed. + */ + public void copyAllToImage(IPath destination, ImageFileFormat format, IProgressMonitor monitor) + throws CoreException { + IPath leftPath = getValidPath(destination, leftDiagramEditPart, format); + copyToImage(leftDiagramEditPart, leftPath, format, monitor); + IPath rightPath = getValidPath(destination, rightDiagramEditPart, format); + copyToImage(rightDiagramEditPart, rightPath, format, monitor); + try { + appendImages(destination, leftPath, rightPath, format, monitor); + } catch (CoreException | IOException e) { + e.printStackTrace(); + } + } + + /** + * Used to recover the files of the two generated images, and create a new one with the two joined images. + * + * @param destination + * the destination folder for the resulting image. + * @param leftPath + * the path to the left image file. + * @param rightPath + * the path to the right image file + * @param format + * the image format. + * @param monitor + * the progress monitor. + * @throws CoreException + * if the export of the image to a file has failed. + * @throws IOException + * if the images on the left or right could not be read. + */ + protected void appendImages(IPath destination, IPath leftPath, IPath rightPath, ImageFileFormat format, + IProgressMonitor monitor) throws CoreException, IOException { + IPath fileName = getValidPath(destination, null, format); + + String exportFormat = ImageExporter.PNG_FILE; + java.awt.Image leftImage = ImageIO.read(leftPath.toFile()); + java.awt.Image rightImage = ImageIO.read(rightPath.toFile()); + if (leftImage instanceof BufferedImage && rightImage instanceof BufferedImage) { + java.awt.Image finalImage = joinImage((BufferedImage)leftImage, (BufferedImage)rightImage); + monitor.worked(1); + ImageExporter.exportToFile(fileName, (BufferedImage)finalImage, exportFormat, monitor, + format.getQuality()); + + } + List<IPath> filesToDelete = new ArrayList<IPath>(); + filesToDelete.add(rightPath); + filesToDelete.add(leftPath); + deleteFiles(filesToDelete); + } + + /** + * Used to delete files. + * + * @param filesToDelete + * the list of files to delete. + */ + protected void deleteFiles(List<IPath> filesToDelete) { + for (IPath path : filesToDelete) { + path.toFile().delete(); + } + } + + /** + * Joins the two images and adds a separator. + * + * @param leftImage + * the image on the left. + * @param rightImage + * the image on the right. + * @return the new generated image. + */ + protected Image joinImage(BufferedImage leftImage, BufferedImage rightImage) { + + int separator = (leftImage.getWidth() + rightImage.getWidth()) / 100; + int wid = leftImage.getWidth() + rightImage.getWidth() + separator; + int height = Math.max(leftImage.getHeight(), rightImage.getHeight()); + + BufferedImage newImage = new BufferedImage(wid, height, leftImage.getType()); + + Graphics2D g2 = newImage.createGraphics(); + + Color oldColor = g2.getColor(); + g2.setPaint(Color.WHITE); + g2.fillRect(0, 0, wid + separator, height); + g2.setPaint(Color.BLACK); + g2.fillRect(leftImage.getWidth(), 0, separator, height); + g2.setColor(oldColor); + + g2.drawImage(leftImage, 0, 0, null); + g2.drawImage(rightImage, leftImage.getWidth() + separator, 0, null); + + g2.dispose(); + return newImage; + } + + /** + * Get the {@link IPath} of the file to write. If the file already exists, it appends a number to its + * name. + * + * @param destination + * the folder where to write the file. + * @param diagramEP + * the {@link DiagramEditPart} used to retrieve the name of the diagram. + * @param format + * the file format. + * @return the path of the file to write. + */ + protected IPath getValidPath(IPath destination, DiagramEditPart diagramEP, ImageFileFormat format) { + String filename = null; + String extension = format.getName().toLowerCase(); + if (diagramEP != null) { + Diagram diagram = (Diagram)diagramEP.getModel(); + DDiagram ddiagram = (DDiagram)diagram.getElement(); + filename = ddiagram.getDescription().getName(); + if (diagramEP == getLeftDiagramEditPart()) { + filename = "left_" + filename; //$NON-NLS-1$ + } else if (diagramEP == getRightDiagramEditPart()) { + filename = "right_" + filename; //$NON-NLS-1$ + } + } else { + filename = "Comparison_Image"; //$NON-NLS-1$ + } + IPath filePath; + final StringBuffer file = new StringBuffer(filename).append('.').append(extension); + + final String filenameWithExtension = validFilename(file.toString()); + if (destination.append(filenameWithExtension).toFile().exists()) { + int version = 1; + + do { + final String newFileName = validFilename(new StringBuffer(filename).append('_') + .append(String.valueOf(version)).append('.').append(extension).toString()); + filePath = destination.append(newFileName); + version++; + } while (filePath.toFile().exists()); + } else { + filePath = destination.append(filenameWithExtension); + } + return filePath; + } + + /** + * Check if the filename is valid or not. + * + * @param filename + * the filename to check. + * @return a valid filename + */ + protected String validFilename(String filename) { + final FileUtil util = new FileUtil(filename); + if (util.isValid()) { + return filename; + } else { + return util.getValidFilename(); + } + } +}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/image/EMFCompareDiagramImageGenerator.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/image/EMFCompareDiagramImageGenerator.java new file mode 100644 index 0000000..e2a5b5e --- /dev/null +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui.sirius/src/org/eclipse/emf/compare/diagram/ide/ui/sirius/internal/tools/image/EMFCompareDiagramImageGenerator.java
@@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2020 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.emf.compare.diagram.ide.ui.sirius.internal.tools.image; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator; +import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide; +import org.eclipse.gef.LayerConstants; +import org.eclipse.gef.editparts.LayerManager; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.sirius.diagram.ui.tools.internal.render.SiriusDiagramImageGenerator; +import org.eclipse.swt.graphics.Rectangle; + +/** + * This class is used to draw the {@link DiagramEditPart} in an image. Any existing Phantoms and Markers + * decorators in the ContentMergeViewer are added to the generated images. + * + * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a> + */ +@SuppressWarnings("restriction") +public class EMFCompareDiagramImageGenerator extends SiriusDiagramImageGenerator { + + /** + * The {@link AbstractDecorator}s to draw. + */ + private List<AbstractDecorator> decorators; + + /** + * The layer on which the {@link AbstractDecorator}s are drawn. + */ + private IFigure layer; + + /** + * Constructor. + * + * @param dgrmEP + * the {@link DiagramEditPart} to draw. + * @param abstractDecorators + * the list of all {@link AbstractDecorator}s + * @param side + * the list of all decorators on each side of the ContentMergeViewer. + */ + public EMFCompareDiagramImageGenerator(DiagramEditPart dgrmEP, + Collection<AbstractDecorator> abstractDecorators, MergeViewerSide side) { + super(dgrmEP); + decorators = new ArrayList<>(); + for (AbstractDecorator decorator : abstractDecorators) { + if (decorator.getSide() == side) { + decorators.add(decorator); + } + } + layer = getLayer(); + } + + /** + * Get the layer on which the {@link DiagramEditPart} is represented on the ContentMergeViewer. + * + * @return the returns the layer corresponding to the {@link DiagramEditPart}. + */ + protected IFigure getLayer() { + return LayerManager.Helper.find(getDiagramEditPart()).getLayer(LayerConstants.PRINTABLE_LAYERS); + } + + /** + * {@inheritDoc} Overridden to draw the {@link AbstractDecorator}s. + */ + @SuppressWarnings("rawtypes") + @Override + protected void renderToGraphics(Graphics graphics, Point translateOffset, List editparts) { + super.renderToGraphics(graphics, translateOffset, editparts); + for (AbstractDecorator decorator : decorators) { + paintFigure(graphics, decorator.getFigure()); + } + } + + /** + * {@inheritDoc} Overridden to get the layer size. + */ + @SuppressWarnings("rawtypes") + @Override + public Rectangle calculateImageRectangle(List editparts) { + return new Rectangle(layer.getBounds().x, layer.getBounds().y, layer.getBounds().width, + layer.getBounds().height); + } +}
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java index 1043020..b677504 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/AbstractDecoratorManager.java
@@ -54,7 +54,7 @@ * * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a> */ - protected abstract class AbstractDecorator { + public abstract class AbstractDecorator { /** The reference <code>View</code> for this decorator. */ protected View fOriginView;
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java index 7b8ebb3..51f4ecc 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DecoratorsManager.java
@@ -10,7 +10,11 @@ *******************************************************************************/ package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram; +import java.util.ArrayList; +import java.util.Collection; + import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator; import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration; import org.eclipse.emf.compare.rcp.ui.mergeviewer.ICompareColor; @@ -96,4 +100,16 @@ fPhantomManager.removeAll(); } + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#getAllDecorators() + */ + public Collection<AbstractDecorator> getAllDecorators() { + Collection<AbstractDecorator> decorators = new ArrayList<AbstractDecorator>(); + decorators.addAll(fMarkerManager.getAllDecorators()); + decorators.addAll(fPhantomManager.getAllDecorators()); + return decorators; + } + }
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java index 7280677..6e49ea3 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java
@@ -93,6 +93,15 @@ } /** + * Get the {@link DecoratorsManager}. + * + * @return the {@link DecoratorsManager}. + */ + public DecoratorsManager getDecoratorsManager() { + return fDecoratorsManager; + } + + /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#getAncestorMergeViewer()
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramMergeViewer.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramMergeViewer.java index 38bb1ff..314c222 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramMergeViewer.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramMergeViewer.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2017 Obeo and others. + * Copyright (c) 2013, 2020 Obeo 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 @@ -52,7 +52,7 @@ * * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a> */ -class DiagramMergeViewer extends AbstractGraphicalMergeViewer { +public class DiagramMergeViewer extends AbstractGraphicalMergeViewer { /** * Selection manager to forbid manual selections on graphical objects.
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java index f5e3a5b..0bbd141 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/IDecoratorManager.java
@@ -10,7 +10,10 @@ *******************************************************************************/ package org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram; +import java.util.Collection; + import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.AbstractDecoratorManager.AbstractDecorator; /** * Interface for the management of decorators. @@ -53,4 +56,10 @@ */ void removeAll(); + /** + * Get all registered decorators. + * + * @return the list of all decorators. + */ + Collection<AbstractDecorator> getAllDecorators(); }
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java index 0075fe6..4d052db 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/MarkerManager.java
@@ -279,4 +279,17 @@ } } + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#getAllDecorators() + */ + public Collection<AbstractDecorator> getAllDecorators() { + Collection<AbstractDecorator> markers = new ArrayList<AbstractDecorator>(); + for (AbstractDecorator marker : fMarkerRegistry.values()) { + markers.add(marker); + } + return markers; + } + }
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java index e72faa7..baf8275 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/PhantomManager.java
@@ -821,4 +821,17 @@ handleDeleteDecorator(phantom, phantom.getLayer(), phantom.getFigure()); } } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.diagram.ide.ui.internal.contentmergeviewer.diagram.IDecoratorManager#getAllDecorators() + */ + public Collection<AbstractDecorator> getAllDecorators() { + Collection<AbstractDecorator> phantoms = new ArrayList<AbstractDecorator>(); + for (AbstractDecorator phantom : fPhantomRegistry.values()) { + phantoms.add(phantom); + } + return phantoms; + } }