blob: d6dabaeffdd52c380aff3bcb4010be09a88df9f3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v0.5
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v05.html
*
* Contributors:
* IBM - Initial implementation
******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.actions;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer;
import org.eclipse.team.internal.ccvs.ui.Policy;
/**
* This class represents an action performed on a local CVS workspace
*/
public abstract class WorkspaceAction extends CVSAction {
/**
* @see org.eclipse.team.internal.ccvs.ui.actions.CVSAction#beginExecution(IAction)
*/
protected boolean beginExecution(IAction action) throws TeamException {
if (super.beginExecution(action)) {
// Ensure that the required sync info is loaded
if (requiresLocalSyncInfo()) {
if (!ensureSyncInfoLoaded(getSelectedResources())) {
return false;
}
if (!isEnabled()) {
MessageDialog.openInformation(getShell(), Policy.bind("CVSAction.disabledTitle"), Policy.bind("CVSAction.disabledMessage")); //$NON-NLS-1$ //$NON-NLS-2$
return false;
}
}
return true;
} else {
return false;
}
}
/**
* Return true if the sync info is loaded for all selected resources.
* The purpose of this method is to allow enablement code to be as fast
* as possible. If the sync info is not loaded, the menu should be enabled
* and, if choosen, the action will verify that it is indeed enabled before
* performing the associated operation
*/
protected boolean isSyncInfoLoaded(IResource[] resources) throws CVSException {
return EclipseSynchronizer.getInstance().isSyncInfoLoaded(resources, getEnablementDepth());
}
/**
* Returns the resource depth of the action for use in determining if the required
* sync info is loaded. The default is IResource.DEPTH_INFINITE. Sunclasses can override
* as required.
*/
protected int getActionDepth() {
return IResource.DEPTH_INFINITE;
}
/**
* Returns the resource depth of the action enablement for use in determining if the required
* sync info is loaded. The default is IResource.DEPTH_ZERO. Sunclasses can override
* as required.
*/
protected int getEnablementDepth() {
return IResource.DEPTH_ZERO;
}
/**
* Ensure that the sync info for all the provided resources has been loaded.
* If an out-of-sync resource is found, prompt to refresh all the projects involved.
*/
protected boolean ensureSyncInfoLoaded(IResource[] resources) throws CVSException {
boolean keepTrying = true;
while (keepTrying) {
try {
EclipseSynchronizer.getInstance().ensureSyncInfoLoaded(resources, getActionDepth());
keepTrying = false;
} catch (CVSException e) {
if (e.getStatus().getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) {
// determine the projects of the resources involved
Set projects = new HashSet();
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
projects.add(resource.getProject());
}
// prompt to refresh
if (promptToRefresh(getShell(), (IResource[]) projects.toArray(new IResource[projects.size()]), e.getStatus())) {
for (Iterator iter = projects.iterator();iter.hasNext();) {
IProject project = (IProject) iter.next();
try {
project.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException coreException) {
throw CVSException.wrapException(coreException);
}
}
} else {
return false;
}
} else {
throw e;
}
}
}
return true;
}
/**
* Override to ensure that the sync info is available before performing the
* real <code>isEnabled()</code> test.
*
* @see org.eclipse.team.internal.ui.actions.TeamAction#setActionEnablement(IAction)
*/
protected void setActionEnablement(IAction action) {
try {
boolean requires = requiresLocalSyncInfo();
if (!requires || (requires && isSyncInfoLoaded(getSelectedResources()))) {
super.setActionEnablement(action);
} else {
// If the sync info is not loaded, enable the menu item
// Performing the action will ensure that the action should really
// be enabled before anything else is done
action.setEnabled(true);
}
} catch (CVSException e) {
// We couldn't determine if the sync info was loaded.
// Enable the action so that performing the action will
// reveal the error to the user.
action.setEnabled(true);
}
}
/**
* Return true if the action requires the sync info for the selected resources.
* If the sync info is required, the real enablement code will only be run if
* the sync info is loaded from disc. Otherwise, the action is enabled and
* performing the action will load the sync info and verify that the action is truely
* enabled before doing anything else.
*
* This implementation returns <code>true</code>. Subclasses must override if they do
* not require the sync info of the selected resources.
*
* @return boolean
*/
protected boolean requiresLocalSyncInfo() {
return true;
}
protected boolean promptToRefresh(final Shell shell, final IResource[] resources, final IStatus status) {
final boolean[] result = new boolean[] { false};
Runnable runnable = new Runnable() {
public void run() {
Shell shellToUse = shell;
if (shell == null) {
shellToUse = new Shell(Display.getCurrent());
}
String question;
if (resources.length == 1) {
question = Policy.bind("CVSAction.refreshQuestion", status.getMessage(), resources[0].getFullPath().toString()); //$NON-NLS-1$
} else {
question = Policy.bind("CVSAction.refreshMultipleQuestion", status.getMessage()); //$NON-NLS-1$
}
result[0] = MessageDialog.openQuestion(shellToUse, Policy.bind("CVSAction.refreshTitle"), question); //$NON-NLS-1$
}
};
Display.getDefault().syncExec(runnable);
return result[0];
}
/**
* Most CVS workspace actions modify the workspace and thus should
* save dirty editors.
* @see org.eclipse.team.internal.ccvs.ui.actions.CVSAction#needsToSaveDirtyEditors()
*/
protected boolean needsToSaveDirtyEditors() {
return true;
}
/*
* @see TeamAction#isEnabled()
*/
protected boolean isEnabled() throws TeamException {
return isSelectionNonOverlapping();
}
}