blob: 6e98c6ee43ae1e3802c1e487769db75a7b783d76 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ui.synchronize;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.team.core.diff.IDiff;
import org.eclipse.team.core.diff.IThreeWayDiff;
import org.eclipse.team.core.mapping.IResourceDiff;
import org.eclipse.team.core.synchronize.SyncInfoSet;
import org.eclipse.team.internal.ui.TeamUIPlugin;
import org.eclipse.team.internal.ui.mapping.CommonViewerAdvisor;
import org.eclipse.team.ui.synchronize.ISynchronizeModelElement;
import org.eclipse.team.ui.synchronize.ISynchronizePage;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
import org.eclipse.team.ui.synchronize.ISynchronizePageSite;
import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
import org.eclipse.team.ui.synchronize.SynchronizePageActionGroup;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.actions.ActionContext;
/**
* Concrete implementation of the ISynchronizePageConfiguration. It
* extends SynchronizePageActionGroup in order to delegate action group
* operations.
*
* @since 3.0
*/
public class SynchronizePageConfiguration extends SynchronizePageActionGroup implements ISynchronizePageConfiguration {
/**
* Property constant for the page's viewer input which is
* an instance of <code>ISynchronizeModelElement</code>.
* This property can be queried by clients but should not be
* set.
*/
public static final String P_MODEL = TeamUIPlugin.ID + ".P_MODEL"; //$NON-NLS-1$
/**
* Property constant for the page's viewer advisor which is
* an instance of <code>StructuredViewerAdvisor</code>.
* The page's viewer can be obtained from the advisor.
* This property can be queried by clients but should not be
* set.
*/
public static final String P_ADVISOR = TeamUIPlugin.ID + ".P_ADVISOR"; //$NON-NLS-1$
/**
* Property constant for the page's navigator
* an instance of <code>INavigable</code>.
* This property can be queried by clients and can be set. By default
* the advisors navigator will be used.
*/
public static final String P_NAVIGATOR = TeamUIPlugin.ID + ".P_NAVIGATOR"; //$NON-NLS-1$
/**
* Property constant for the compare editor inputs navigator
* an instance of <code>INavigable</code>.
* This property can be queried by clients and can be set.
*/
public static final String P_INPUT_NAVIGATOR = TeamUIPlugin.ID + ".P_INPUT_NAVIGATOR"; //$NON-NLS-1$
/**
* Property constant for the page's model manager which is
* an instance of <code>SynchronizeModelManager</code>.
* This property can be queried by clients but should not be
* set.
*/
public static final String P_MODEL_MANAGER = TeamUIPlugin.ID + ".P_MODEL_MANAGER"; //$NON-NLS-1$
/**
* Property that gives access to a set the
* contains all out-of-sync resources for the participant
* in the selected working set.
*/
public static final String P_WORKING_SET_SYNC_INFO_SET = TeamUIPlugin.ID + ".P_WORKING_SET_SYNC_INFO_SET"; //$NON-NLS-1$
/**
* Property that gives access to a set the
* contains all out-of-sync resources for the participant
* before any filtering (working set or modes) is applied.
*/
public static final String P_PARTICIPANT_SYNC_INFO_SET = TeamUIPlugin.ID + ".P_PARTICIPANT_SYNC_INFO_SET"; //$NON-NLS-1$
/**
* The hidden configuration property that opens the current selection in the
* page. The registered <code>IAction</code> is invoked on a single or
* double click depending on the open strategy chosen by the user.
*/
public static final String P_OPEN_ACTION = TeamUIPlugin.ID + ".P_OPEN_ACTION"; //$NON-NLS-1$
/**
* Property constant for the style of the view to be used by the page.
*/
public static final String P_VIEWER_STYLE = TeamUIPlugin.ID + ".P_VIEWER_STYLE"; //$NON-NLS-1$
public static final int CHECKBOX = TreeViewerAdvisor.CHECKBOX;
// State flags
private static final int UNINITIALIZED = 0;
private static final int INITIALIZED = 1;
private static final int DISPOSED = 2;
private ISynchronizeParticipant participant;
private ISynchronizePageSite site;
private ListenerList<IPropertyChangeListener> propertyChangeListeners = new ListenerList<>(ListenerList.IDENTITY);
private ListenerList<SynchronizePageActionGroup> actionContributions = new ListenerList<>(ListenerList.IDENTITY);
private Map<String, Object> properties = new HashMap<>();
private int actionState = UNINITIALIZED;
private ISynchronizePage page;
private IRunnableContext context;
/**
* Create a configuration for creating a page from the given participant.
* @param participant the participant whose page is being configured
*/
public SynchronizePageConfiguration(ISynchronizeParticipant participant) {
this.participant = participant;
setProperty(P_CONTEXT_MENU, DEFAULT_CONTEXT_MENU);
setProperty(P_TOOLBAR_MENU, DEFAULT_TOOLBAR_MENU);
setProperty(P_VIEW_MENU, DEFAULT_VIEW_MENU);
setProperty(P_COMPARISON_TYPE, THREE_WAY);
}
@Override
public ISynchronizeParticipant getParticipant() {
return participant;
}
@Override
public ISynchronizePageSite getSite() {
return site;
}
/**
* Set the site that is associated with the page that was
* configured using this configuration.
* @param site a synchronize page site
*/
public void setSite(ISynchronizePageSite site) {
this.site = site;
}
@Override
public void addPropertyChangeListener(IPropertyChangeListener listener) {
synchronized(propertyChangeListeners) {
propertyChangeListeners.add(listener);
}
}
@Override
public void removePropertyChangeListener(IPropertyChangeListener listener) {
synchronized(propertyChangeListeners) {
propertyChangeListeners.remove(listener);
}
}
@Override
public void setProperty(String key, Object newValue) {
Object oldValue = properties.get(key);
if (page == null || page.aboutToChangeProperty(this, key, newValue)) {
properties.put(key, newValue);
if (oldValue == null || !oldValue.equals(newValue))
firePropertyChange(key, oldValue, newValue);
}
}
@Override
public Object getProperty(String key) {
return properties.get(key);
}
@Override
public void addActionContribution(SynchronizePageActionGroup contribution) {
int currentActionState;
synchronized(actionContributions) {
// Determine the action state while locked so we handle the addition properly below
currentActionState = actionState;
if (currentActionState != DISPOSED)
actionContributions.add(contribution);
}
if (currentActionState == INITIALIZED) {
// This is tricky because we are doing the initialize while not locked.
// It is possible that another thread is concurrently disposing the contributions
// but we can't lock while calling client code. We'll change for DISPOSE after
// we initialize and, if we are disposed, we dispose this one, just in case.
contribution.initialize(this);
if (actionState == DISPOSED) {
contribution .dispose();
}
} else if (currentActionState == DISPOSED) {
contribution.dispose();
}
}
@Override
public void removeActionContribution(SynchronizePageActionGroup contribution) {
synchronized(actionContributions) {
actionContributions.remove(contribution);
}
}
private void firePropertyChange(String key, Object oldValue, Object newValue) {
Object[] listeners;
synchronized(propertyChangeListeners) {
listeners = propertyChangeListeners.getListeners();
}
final PropertyChangeEvent event = new PropertyChangeEvent(this, key, oldValue, newValue);
for (int i = 0; i < listeners.length; i++) {
final IPropertyChangeListener listener = (IPropertyChangeListener)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Error is logged by platform
}
@Override
public void run() throws Exception {
listener.propertyChange(event);
}
});
}
}
@Override
public void initialize(final ISynchronizePageConfiguration configuration) {
super.initialize(configuration);
// need to synchronize here to ensure that actions that are added concurrently also get initialized
final Object[] listeners;
synchronized(actionContributions) {
if (actionState != UNINITIALIZED) {
// Initialization has already taken place so just return.
return;
}
actionState = INITIALIZED;
listeners = actionContributions.getListeners();
}
for (int i= 0; i < listeners.length; i++) {
final SynchronizePageActionGroup contribution = (SynchronizePageActionGroup)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Logged by Platform
}
@Override
public void run() throws Exception {
contribution.initialize(configuration);
}
});
}
}
@Override
public void setContext(final ActionContext context) {
super.setContext(context);
final Object[] listeners;
synchronized(actionContributions) {
listeners = actionContributions.getListeners();
}
for (int i= 0; i < listeners.length; i++) {
final SynchronizePageActionGroup contribution = (SynchronizePageActionGroup)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Logged by Platform
}
@Override
public void run() throws Exception {
contribution.setContext(context);
}
});
}
}
/**
* Callback invoked from the advisor each time the context menu is
* about to be shown.
* @param manager the context menu manager
*/
@Override
public void fillContextMenu(final IMenuManager manager) {
final Object[] listeners;
synchronized(actionContributions) {
listeners = actionContributions.getListeners();
}
for (int i= 0; i < listeners.length; i++) {
final SynchronizePageActionGroup contribution = (SynchronizePageActionGroup)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Logged by Platform
}
@Override
public void run() throws Exception {
contribution.fillContextMenu(manager);
}
});
}
}
/**
* Callback invoked from the page to fill the action bars.
* @param actionBars the action bars of the view
*/
@Override
public void fillActionBars(final IActionBars actionBars) {
if (actionState == UNINITIALIZED) {
initialize(this);
}
final Object[] listeners;
synchronized(actionContributions) {
listeners = actionContributions.getListeners();
}
for (int i= 0; i < listeners.length; i++) {
final SynchronizePageActionGroup contribution = (SynchronizePageActionGroup)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Logged by Platform
}
@Override
public void run() throws Exception {
contribution.fillActionBars(actionBars);
}
});
}
}
@Override
public void updateActionBars() {
final Object[] listeners;
synchronized(actionContributions) {
listeners = actionContributions.getListeners();
}
for (int i= 0; i < listeners.length; i++) {
final SynchronizePageActionGroup contribution = (SynchronizePageActionGroup)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Logged by Platform
}
@Override
public void run() throws Exception {
contribution.updateActionBars();
}
});
}
}
@Override
public void modelChanged(final ISynchronizeModelElement root) {
final Object[] listeners;
synchronized(actionContributions) {
listeners = actionContributions.getListeners();
}
for (int i= 0; i < listeners.length; i++) {
final SynchronizePageActionGroup contribution = (SynchronizePageActionGroup)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Logged by Platform
}
@Override
public void run() throws Exception {
contribution.modelChanged(root);
}
});
}
}
@Override
public void dispose() {
super.dispose();
final Object[] listeners;
synchronized(actionContributions) {
listeners = actionContributions.getListeners();
actionState = DISPOSED;
}
for (int i= 0; i < listeners.length; i++) {
final SynchronizePageActionGroup contribution = (SynchronizePageActionGroup)listeners[i];
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
// Logged by Platform
}
@Override
public void run() throws Exception {
contribution.dispose();
}
});
}
}
@Override
public void setMenuGroups(String menuPropertyId, String[] groups) {
setProperty(menuPropertyId, groups);
}
@Override
public void addMenuGroup(String menuPropertyId, String groupId) {
String[] menuGroups = (String[])getProperty(menuPropertyId);
if (menuGroups == null) {
menuGroups = getDefault(menuPropertyId);
}
String[] newGroups = new String[menuGroups.length + 1];
System.arraycopy(menuGroups, 0, newGroups, 0, menuGroups.length);
newGroups[menuGroups.length] = groupId;
setProperty(menuPropertyId, newGroups);
}
@Override
public boolean hasMenuGroup(String menuPropertyId, String groupId) {
String[] groups = (String[])getProperty(menuPropertyId);
if (groups == null) {
groups = getDefault(menuPropertyId);
}
for (int i = 0; i < groups.length; i++) {
String string = groups[i];
if (string.equals(groupId)) return true;
}
return false;
}
protected String[] getDefault(String menuPropertyId) {
if (menuPropertyId.equals(P_CONTEXT_MENU)) {
return DEFAULT_CONTEXT_MENU;
} else if (menuPropertyId.equals(P_VIEW_MENU)) {
return DEFAULT_VIEW_MENU;
} else if (menuPropertyId.equals(P_TOOLBAR_MENU)) {
return DEFAULT_TOOLBAR_MENU;
} else {
return new String[0];
}
}
@Override
public void addLabelDecorator(ILabelDecorator decorator) {
ILabelDecorator[] decorators = (ILabelDecorator[])getProperty(P_LABEL_DECORATORS);
if (decorators == null) {
decorators = new ILabelDecorator[0];
}
// Ensure we don't have it registered already
for (int i = 0; i < decorators.length; i++) {
ILabelDecorator d = decorators[i];
if (d == decorator) {
return;
}
}
ILabelDecorator[] newDecorators = new ILabelDecorator[decorators.length + 1];
System.arraycopy(decorators, 0, newDecorators, 0, decorators.length);
newDecorators[decorators.length] = decorator;
setProperty(P_LABEL_DECORATORS, newDecorators);
}
/**
* @param group
* @return the group id
*/
public String getGroupId(String group) {
String id = getParticipant().getId();
if (getParticipant().getSecondaryId() != null) {
id += "."; //$NON-NLS-1$
id += getParticipant().getSecondaryId();
}
return id + "." + group; //$NON-NLS-1$
}
@Override
public int getMode() {
Object o = getProperty(P_MODE);
if (o instanceof Integer) {
return ((Integer)o).intValue();
}
return 0;
}
@Override
public void setMode(int mode) {
if (isModeSupported(mode))
setProperty(P_MODE, Integer.valueOf(mode));
}
public boolean isModeSupported(int mode) {
return (getSupportedModes() & mode) > 0;
}
@Override
public int getSupportedModes() {
Object o = getProperty(P_SUPPORTED_MODES);
if (o instanceof Integer) {
return ((Integer)o).intValue();
}
return 0;
}
@Override
public void setSupportedModes(int modes) {
setProperty(P_SUPPORTED_MODES, Integer.valueOf(modes));
}
/**
* @return Returns the page.
*/
@Override
public ISynchronizePage getPage() {
return page;
}
/**
* @param page The page to set.
*/
@Override
public void setPage(ISynchronizePage page) {
this.page = page;
}
/**
* @return the viewer style
*/
public int getViewerStyle() {
Object o = getProperty(P_VIEWER_STYLE);
if (o instanceof Integer) {
return ((Integer)o).intValue();
}
return 0;
}
/**
* @param style
*/
public void setViewerStyle(int style) {
setProperty(P_VIEWER_STYLE, Integer.valueOf(style));
}
@Override
public SyncInfoSet getSyncInfoSet() {
Object o = getProperty(P_SYNC_INFO_SET);
if (o instanceof SyncInfoSet) {
return (SyncInfoSet)o;
}
return null;
}
@Override
public String getComparisonType() {
return (String)getProperty(P_COMPARISON_TYPE);
}
@Override
public void setComparisonType(String type) {
setProperty(P_COMPARISON_TYPE,type);
}
@Override
public void setRunnableContext(IRunnableContext context) {
this.context = context;
}
@Override
public IRunnableContext getRunnableContext() {
return context;
}
@Override
public String getViewerId() {
String viewerId = (String)getProperty(P_VIEWER_ID);
if (viewerId != null)
return viewerId;
return CommonViewerAdvisor.TEAM_NAVIGATOR_CONTENT;
}
/**
* Return whether the given node is visible in the page based
* on the mode in the configuration.
* @param node a diff node
* @return whether the given node is visible in the page
*/
public boolean isVisible(IDiff node) {
if (getComparisonType() == ISynchronizePageConfiguration.THREE_WAY
&& node instanceof IThreeWayDiff) {
IThreeWayDiff twd = (IThreeWayDiff) node;
return includeDirection(twd.getDirection());
}
return getComparisonType() == ISynchronizePageConfiguration.TWO_WAY && node instanceof IResourceDiff;
}
/**
* Return whether elements with the given direction should be included in
* the contents.
*
* @param direction
* the synchronization direction
* @return whether elements with the given synchronization kind should be
* included in the contents
*/
public boolean includeDirection(int direction) {
int mode = getMode();
switch (mode) {
case ISynchronizePageConfiguration.BOTH_MODE:
return true;
case ISynchronizePageConfiguration.CONFLICTING_MODE:
return direction == IThreeWayDiff.CONFLICTING;
case ISynchronizePageConfiguration.INCOMING_MODE:
return direction == IThreeWayDiff.CONFLICTING || direction == IThreeWayDiff.INCOMING;
case ISynchronizePageConfiguration.OUTGOING_MODE:
return direction == IThreeWayDiff.CONFLICTING || direction == IThreeWayDiff.OUTGOING;
default:
break;
}
return true;
}
public ILabelDecorator getLabelDecorator() {
ILabelDecorator[] decorators = (ILabelDecorator[])getProperty(ISynchronizePageConfiguration.P_LABEL_DECORATORS);
if (decorators == null) {
return null;
}
return new MultiLabelDecorator(decorators);
}
}