/*******************************************************************************
* 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);
}
}
}