blob: 67fffa8f80c7aa074f2591dcab77a804133113f7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2015 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
* Michael Borkowski - bug 462237
* Martin Fleck - bug 483798
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions;
import static com.google.common.collect.Iterables.addAll;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import java.util.List;
import org.eclipse.compare.INavigatable;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.command.ICompareCopyCommand;
import org.eclipse.emf.compare.domain.ICompareEditingDomain;
import org.eclipse.emf.compare.domain.IMergeRunnable;
import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages;
import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin;
import org.eclipse.emf.compare.ide.ui.internal.preferences.EMFCompareUIPreferences;
import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.Navigatable;
import org.eclipse.emf.compare.internal.merge.MergeMode;
import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
import org.eclipse.emf.compare.merge.IMerger;
import org.eclipse.emf.compare.merge.IMerger.Registry;
import org.eclipse.emf.compare.provider.ITooltipLabelProvider;
import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration;
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroup;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.tree.TreeNode;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.actions.BaseSelectionListenerAction;
import org.eclipse.ui.plugin.AbstractUIPlugin;
/**
* Abstract Action that manages a merge of a difference in case of both sides of the comparison are editable.
*
* @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
* @since 3.0
*/
public class MergeAction extends BaseSelectionListenerAction {
protected static final Function<? super Adapter, ? extends Notifier> ADAPTER__TARGET = new Function<Adapter, Notifier>() {
public Notifier apply(Adapter adapter) {
return adapter.getTarget();
}
};
protected final Registry mergerRegistry;
protected ICompareEditingDomain editingDomain;
private boolean leftToRight;
protected IMergeRunnable mergeRunnable;
private final List<Diff> selectedDifferences;
private final INavigatable navigatable;
/**
* The merge mode used for the comparison.
*/
private final MergeMode selectedMode;
/**
* The adapter factory for the comparison.
*/
private AdapterFactory adapterFactory;
private IDiffRelationshipComputer diffRelationshipComputer;
private boolean isMirrored;
private final boolean isLeftEditable;
private final boolean isRightEditable;
/**
* Constructor.
*
* @param configuration
* The compare configuration object.
*/
public MergeAction(IEMFCompareConfiguration compareConfiguration, IMerger.Registry mergerRegistry,
MergeMode mode, INavigatable navigatable) {
super(""); //$NON-NLS-1$
adapterFactory = compareConfiguration.getAdapterFactory();
diffRelationshipComputer = compareConfiguration.getDiffRelationshipComputer();
isLeftEditable = compareConfiguration.isLeftEditable();
isRightEditable = compareConfiguration.isRightEditable();
this.navigatable = navigatable;
Preconditions.checkNotNull(mode);
// at least should be editable
Preconditions.checkState(isLeftEditable || isRightEditable);
// if left and right editable, the only accepted mode are LtR or RtL
if (isLeftEditable && isRightEditable) {
Preconditions.checkState(mode == MergeMode.LEFT_TO_RIGHT || mode == MergeMode.RIGHT_TO_LEFT);
}
// if mode is accept or reject, left and right can't be both read only (no action should be created in
// this case) and can't be both editable.
if (isLeftEditable != isRightEditable) {
Preconditions.checkState(mode == MergeMode.ACCEPT || mode == MergeMode.REJECT);
}
this.editingDomain = compareConfiguration.getEditingDomain();
this.mergerRegistry = mergerRegistry;
this.leftToRight = mode.isLeftToRight(isLeftEditable, isRightEditable);
this.mergeRunnable = createMergeRunnable(mode, isLeftEditable, isRightEditable,
diffRelationshipComputer);
this.selectedDifferences = newArrayList();
this.selectedMode = mode;
initToolTipAndImage(mode);
}
public MergeAction(IEMFCompareConfiguration compareConfiguration, IMerger.Registry mergerRegistry,
MergeMode mode, INavigatable navigatable, IStructuredSelection selection) {
this(compareConfiguration, mergerRegistry, mode, navigatable);
updateSelection(selection);
}
protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean leftEditable, boolean rightEditable,
IDiffRelationshipComputer relationshipComputer) {
return new MergeRunnableImpl(leftEditable, rightEditable, mode, relationshipComputer);
}
protected void initToolTipAndImage(MergeMode mode) {
switch (mode) {
case LEFT_TO_RIGHT:
setText(EMFCompareIDEUIMessages.getString("merged.to.right.tooltip")); //$NON-NLS-1$
setToolTipText(EMFCompareIDEUIMessages.getString("merged.to.right.tooltip")); //$NON-NLS-1$
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID,
"icons/full/toolb16/merge_to_right.gif")); //$NON-NLS-1$
break;
case RIGHT_TO_LEFT:
setText(EMFCompareIDEUIMessages.getString("merged.to.left.tooltip")); //$NON-NLS-1$
setToolTipText(EMFCompareIDEUIMessages.getString("merged.to.left.tooltip")); //$NON-NLS-1$
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID,
"icons/full/toolb16/merge_to_left.gif")); //$NON-NLS-1$
break;
case ACCEPT:
setText(EMFCompareIDEUIMessages.getString("accept.change.tooltip")); //$NON-NLS-1$
setToolTipText(EMFCompareIDEUIMessages.getString("accept.change.tooltip")); //$NON-NLS-1$
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID,
"icons/full/toolb16/accept_change.gif")); //$NON-NLS-1$
break;
case REJECT:
setText(EMFCompareIDEUIMessages.getString("reject.change.tooltip")); //$NON-NLS-1$
setToolTipText(EMFCompareIDEUIMessages.getString("reject.change.tooltip")); //$NON-NLS-1$
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID,
"icons/full/toolb16/reject_change.gif")); //$NON-NLS-1$
break;
default:
throw new IllegalStateException();
}
}
/**
* This method is used to created contextual tooltips.
*/
protected void contextualizeTooltip() {
if (this.selectedDifferences.size() > 1) {
// multiple selection
setMultipleTooltip(this.selectedMode);
} else if (this.selectedDifferences.isEmpty()) {
// no selection
initToolTipAndImage(this.selectedMode);
} else {
Diff diff = this.selectedDifferences.get(0);
Object adapter = adapterFactory.adapt(diff, ITooltipLabelProvider.class);
if (adapter instanceof ITooltipLabelProvider) {
String tooltip = ((ITooltipLabelProvider)adapter).getTooltip(this.selectedMode);
setToolTipText(tooltip);
} else {
initToolTipAndImage(this.selectedMode);
}
}
}
/**
* Set the tooltips for multiple selection.
*
* @param mode
* The comparison mode
*/
private void setMultipleTooltip(MergeMode mode) {
switch (mode) {
case LEFT_TO_RIGHT:
setToolTipText(EMFCompareIDEUIMessages.getString("merged.multiple.to.right.tooltip")); //$NON-NLS-1$
break;
case RIGHT_TO_LEFT:
setToolTipText(EMFCompareIDEUIMessages.getString("merged.multiple.to.left.tooltip")); //$NON-NLS-1$
break;
case ACCEPT:
setToolTipText(EMFCompareIDEUIMessages.getString("accept.multiple.changes.tooltip")); //$NON-NLS-1$
break;
case REJECT:
setToolTipText(EMFCompareIDEUIMessages.getString("reject.multiple.changes.tooltip")); //$NON-NLS-1$
break;
default:
throw new IllegalStateException();
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.action.Action#run()
*/
@Override
public void run() {
ICompareCopyCommand mergeCommand = editingDomain.createCopyCommand(selectedDifferences, leftToRight,
mergerRegistry, mergeRunnable);
editingDomain.getCommandStack().execute(mergeCommand);
if (navigatable != null && EMFCompareIDEUIPlugin.getDefault().getPreferenceStore()
.getBoolean(EMFCompareUIPreferences.SELECT_NEXT_UNRESOLVED_DIFF)) {
// navigator is null in MergeAllNonConflictingAction
navigatable.selectChange(Navigatable.NEXT_UNRESOLVED_CHANGE);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.ui.actions.BaseSelectionListenerAction#updateSelection(org.eclipse.jface.viewers.IStructuredSelection)
*/
@Override
protected boolean updateSelection(IStructuredSelection selection) {
addAll(selectedDifferences, getSelectedDifferences(selection));
if (this.adapterFactory != null) {
contextualizeTooltip();
}
return selection.toList().size() == selectedDifferences.size();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.ui.actions.BaseSelectionListenerAction#clearCache()
*/
@Override
protected void clearCache() {
selectedDifferences.clear();
}
protected Iterable<Diff> getSelectedDifferences(IStructuredSelection selection) {
List<?> selectedObjects = selection.toList();
Iterable<Adapter> selectedAdapters = filter(selectedObjects, Adapter.class);
Iterable<Notifier> selectedNotifiers = transform(selectedAdapters, ADAPTER__TARGET);
Iterable<TreeNode> selectedTreeNode = filter(selectedNotifiers, TreeNode.class);
Iterable<EObject> selectedEObjects = transform(selectedTreeNode, IDifferenceGroup.TREE_NODE_DATA);
return filter(selectedEObjects, Diff.class);
}
/**
* @param newValue
*/
public final void setEditingDomain(ICompareEditingDomain editingDomain) {
this.editingDomain = editingDomain;
clearCache();
setEnabled(editingDomain != null && updateSelection(getStructuredSelection()));
}
/**
* Set the adapter factory used by this action.
*
* @param adapterFactory
* adapter factory
*/
public final void setAdapterFactory(AdapterFactory adapterFactory) {
this.adapterFactory = adapterFactory;
if (adapterFactory != null) {
contextualizeTooltip();
}
}
/**
* Refreshes the merge action by re-creating the necessary elements based on the current compare
* configuration.
*/
public void setMirrored(boolean mirrored) {
if (selectedMode == MergeMode.ACCEPT || selectedMode == MergeMode.REJECT) {
return;
}
if (this.isMirrored != mirrored) {
this.isMirrored = mirrored;
if (mirrored) {
MergeMode mirroredMode = selectedMode.inverse();
leftToRight = mirroredMode.isLeftToRight(isRightEditable, isLeftEditable);
mergeRunnable = createMergeRunnable(mirroredMode, isRightEditable, isLeftEditable,
diffRelationshipComputer);
} else {
leftToRight = selectedMode.isLeftToRight(isLeftEditable, isRightEditable);
mergeRunnable = createMergeRunnable(selectedMode, isLeftEditable, isRightEditable,
diffRelationshipComputer);
}
}
}
/**
* @return the leftToRight
*/
protected final boolean isLeftToRight() {
return leftToRight;
}
}