/******************************************************************************* * Copyright (c) 2012, 2016 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Obeo - initial API and implementation * Martin Fleck - bug 483798 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.table; import static com.google.common.base.Predicates.equalTo; import com.google.common.base.Objects; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import java.util.Map; import java.util.ResourceBundle; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.adapterfactory.context.IContextTester; import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration; import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.rcp.ui.contentmergeviewer.accessor.ICompareAccessor; import org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.impl.AbstractMergeViewer; import org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.impl.TableMergeViewer; import org.eclipse.emf.compare.rcp.ui.internal.util.MergeViewerUtil; import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide; import org.eclipse.emf.compare.rcp.ui.mergeviewer.item.IMergeViewerItem; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.IItemFontProvider; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseWheelListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; /** * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a> */ public class TableContentMergeViewer extends EMFCompareContentMergeViewer { /** * Bundle name of the property file containing all displayed strings. */ private static final String BUNDLE_NAME = TableContentMergeViewer.class.getName(); private final ComposedAdapterFactory fAdapterFactory; private double[] fBasicCenterCurve; /** * Call the super constructor. * * @see TableContentMergeViewer */ protected TableContentMergeViewer(Composite parent, EMFCompareConfiguration config) { super(SWT.NONE, ResourceBundle.getBundle(BUNDLE_NAME), config); Map<Object, Object> context = Maps.newLinkedHashMap(); context.put(IContextTester.CTX_COMPARISON, config.getComparison()); fAdapterFactory = new ComposedAdapterFactory( EMFCompareRCPPlugin.getDefault().createFilteredAdapterFactoryRegistry(context)); fAdapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory()); fAdapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory()); buildControl(parent); setContentProvider(new TableContentMergeViewerContentProvider(config)); } /** * {@inheritDoc} * * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handleDispose(org.eclipse.swt.events.DisposeEvent) */ @Override protected void handleDispose(DisposeEvent event) { fAdapterFactory.dispose(); super.handleDispose(event); } /** * {@inheritDoc} * * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#getContents(boolean) */ @Override protected byte[] getContents(boolean left) { return null; } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#getLeftMergeViewer() */ @Override protected TableMergeViewer getLeftMergeViewer() { return (TableMergeViewer)super.getLeftMergeViewer(); } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#getRightMergeViewer() */ @Override protected TableMergeViewer getRightMergeViewer() { return (TableMergeViewer)super.getRightMergeViewer(); } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#getAncestorMergeViewer() */ @Override protected TableMergeViewer getAncestorMergeViewer() { return (TableMergeViewer)super.getAncestorMergeViewer(); } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#createMergeViewer(org.eclipse.swt.widgets.Composite, * org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide) */ @Override protected AbstractMergeViewer createMergeViewer(Composite parent, final MergeViewerSide side) { TableMergeViewer ret = new TableMergeViewer(parent, side, this, getCompareConfiguration()); ret.getStructuredViewer().getTable().getVerticalBar().setVisible(false); ret.setContentProvider(new ArrayContentProvider() { /** * {@inheritDoc} * * @see org.eclipse.jface.viewers.ArrayContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements(Object inputElement) { if (inputElement instanceof ICompareAccessor) { return super.getElements(((ICompareAccessor)inputElement).getItems()); } return super.getElements(inputElement); } }); ret.setLabelProvider(new AdapterFactoryLabelProvider.FontAndColorProvider(fAdapterFactory, ret.getStructuredViewer()) { /** * {@inheritDoc} * * @see org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider#getFont(java.lang.Object, * int) */ @Override public Font getFont(Object object, int columnIndex) { if (object instanceof IMergeViewerItem) { final Object value = ((IMergeViewerItem)object).getSideValue(side); if (value instanceof EObject && ((EObject)value).eIsProxy()) { return getFontFromObject(IItemFontProvider.ITALIC_FONT); } } return super.getFont(object, columnIndex); } /** * {@inheritDoc} * * @see org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider#getColumnText(java.lang.Object, * int) */ @Override public String getColumnText(Object object, int columnIndex) { if (object instanceof IMergeViewerItem) { final String text; IMergeViewerItem mergeViewerItem = (IMergeViewerItem)object; final Object value = mergeViewerItem.getSideValue(side); if (value instanceof EObject && ((EObject)value).eIsProxy()) { text = "proxy : " + ((InternalEObject)value).eProxyURI().toString(); //$NON-NLS-1$ } else if (mergeViewerItem.isInsertionPoint()) { // workaround for 406513: Windows specific issue. Only labels of (Tree/Table)Item are // selectable on Windows platform. The labels of placeholders in (Tree/Table)Viewer // are one whitespace. Placeholder are then selectable at the very left of itself. // Add a 42 whitespaces label to workaround. text = Strings.repeat(" ", 42); //$NON-NLS-1$ } else { text = super.getColumnText(value, columnIndex); } return text; } return super.getColumnText(object, columnIndex); } /** * {@inheritDoc} * * @see org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider#getColumnImage(java.lang.Object, * int) */ @Override public Image getColumnImage(Object object, int columnIndex) { if (object instanceof IMergeViewerItem) { IMergeViewerItem mergeViewerItem = (IMergeViewerItem)object; if (((IMergeViewerItem)object).isInsertionPoint()) { return null; } else { Object sideValue = mergeViewerItem.getSideValue(side); Image superImage = super.getColumnImage(sideValue, columnIndex); if (superImage == null) { return getDefaultImage(sideValue); } else { return superImage; } } } return super.getColumnImage(object, columnIndex); } }); ret.getStructuredViewer().getTable().getVerticalBar().addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { redrawCenterControl(); } }); ret.getStructuredViewer().getTable().addMouseWheelListener(new MouseWheelListener() { public void mouseScrolled(MouseEvent e) { redrawCenterControl(); } }); ret.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { redrawCenterControl(); } }); return ret; } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#paintCenter(org.eclipse.swt.widgets.Canvas, * org.eclipse.swt.graphics.GC) */ @Override protected void paintCenter(GC g) { TableMergeViewer leftMergeViewer = getLeftMergeViewer(); TableMergeViewer rightMergeViewer = getRightMergeViewer(); Table leftTable = leftMergeViewer.getStructuredViewer().getTable(); Table rightTable = rightMergeViewer.getStructuredViewer().getTable(); Rectangle leftClientArea = leftTable.getClientArea(); Rectangle rightClientArea = rightTable.getClientArea(); TableItem[] leftItems = leftTable.getItems(); TableItem[] rightItems = rightTable.getItems(); final ImmutableSet<TableItem> selection = ImmutableSet.copyOf(leftTable.getSelection()); for (TableItem leftItem : leftItems) { final boolean selected = Iterables.any(selection, equalTo(leftItem)); IMergeViewerItem leftData = (IMergeViewerItem)leftItem.getData(); final Diff leftDiff = leftData.getDiff(); if (leftDiff != null) { if (MergeViewerUtil.isVisibleInMergeViewer(leftDiff, getDifferenceGroupProvider(), getDifferenceFilterPredicate()) && !MergeViewerUtil.isMarkAsMerged(leftDiff, leftData, getCompareConfiguration())) { TableItem rightItem = findRightTableItemFromLeftDiff(rightItems, leftDiff, leftData); if (rightItem != null) { final Color strokeColor = getCompareColor().getStrokeColor(leftDiff, isThreeWay(), false, selected); g.setForeground(strokeColor); drawCenterLine(g, leftClientArea, rightClientArea, leftItem, rightItem); } } } } } private void drawCenterLine(GC g, Rectangle leftClientArea, Rectangle rightClientArea, TableItem leftItem, TableItem rightItem) { Control control = getCenterControl(); Point from = new Point(0, 0); Point to = new Point(0, 0); Rectangle leftBounds = leftItem.getBounds(); Rectangle rightBounds = rightItem.getBounds(); from.y = leftBounds.y + (leftBounds.height / 2) - leftClientArea.y + 1 + getLeftMergeViewer().getVerticalOffset(); to.x = control.getBounds().width; to.y = rightBounds.y + (rightBounds.height / 2) - rightClientArea.y + 1 + getRightMergeViewer().getVerticalOffset(); int[] points = getCenterCurvePoints(from, to); for (int i = 1; i < points.length; i++) { g.drawLine(from.x + i - 1, points[i - 1], i, points[i]); } } private TableItem findRightTableItemFromLeftDiff(TableItem[] rightItems, Diff leftDiff, IMergeViewerItem leftData) { TableItem ret = null; for (int i = 0; i < rightItems.length && ret == null; i++) { TableItem rightItem = rightItems[i]; IMergeViewerItem rightData = (IMergeViewerItem)rightItem.getData(); final Diff rightDiff = ((IMergeViewerItem)rightItem.getData()).getDiff(); if (leftDiff == rightDiff) { ret = rightItem; } else if (Objects.equal(rightData.getAncestor(), leftData.getAncestor()) && Objects.equal(rightData.getRight(), leftData.getRight()) && Objects.equal(rightData.getLeft(), leftData.getLeft())) { ret = rightItem; } } return ret; } private int[] getCenterCurvePoints(Point from, Point to) { int startx = from.x; int starty = from.y; int endx = to.x; int endy = to.y; if (fBasicCenterCurve == null) { buildBaseCenterCurve(endx - startx); } double height = endy - starty; height = height / 2; int width = endx - startx; int[] points = new int[width]; for (int i = 0; i < width; i++) { points[i] = (int)(-height * fBasicCenterCurve[i] + height + starty); } return points; } private void buildBaseCenterCurve(int w) { double width = w; fBasicCenterCurve = new double[getCenterWidth()]; for (int i = 0; i < getCenterWidth(); i++) { double r = i / width; fBasicCenterCurve[i] = Math.cos(Math.PI * r); } } }