blob: 1c9baa86c74bb94b327a457dd589dcbeabfa1c95 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Remy Chi Jian Suen <remy.suen@gmail.com>
* - Bug 44162 [Wizards] Define constants for wizard ids of new.file, new.folder, and new.project
*******************************************************************************/
package org.eclipse.ptp.internal.rdt.sync.ui.wizards;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ptp.internal.rdt.sync.ui.RDTSyncUIPlugin;
import org.eclipse.ptp.internal.rdt.sync.ui.handlers.CommonSyncExceptionHandler;
import org.eclipse.ptp.internal.rdt.sync.ui.messages.Messages;
import org.eclipse.ptp.rdt.sync.core.SyncFlag;
import org.eclipse.ptp.rdt.sync.core.SyncManager;
import org.eclipse.ptp.rdt.sync.core.SyncManager.SyncMode;
import org.eclipse.ptp.rdt.sync.ui.ISynchronizeParticipant;
import org.eclipse.ptp.rdt.sync.ui.ISynchronizeWizardExtension;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPerspectiveRegistry;
import org.eclipse.ui.IPluginContribution;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.activities.IActivityManager;
import org.eclipse.ui.activities.IIdentifier;
import org.eclipse.ui.activities.IWorkbenchActivitySupport;
import org.eclipse.ui.activities.WorkbenchActivityHelper;
import org.eclipse.ui.dialogs.WizardNewProjectReferencePage;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.undo.CreateProjectOperation;
import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
import org.eclipse.ui.internal.IPreferenceConstants;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.ide.IDEInternalPreferences;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.StatusUtil;
import org.eclipse.ui.internal.registry.PerspectiveDescriptor;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.statushandlers.StatusAdapter;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard;
/**
* Standard workbench wizard that creates a new project resource in the
* workspace.
* <p>
* This class may be instantiated and used without further configuration; this class is not intended to be subclassed.
* </p>
* <p>
* Example:
*
* <pre>
* IWorkbenchWizard wizard = new NewSyncProjectWizard();
* wizard.init(workbench, selection);
* WizardDialog dialog = new WizardDialog(shell, wizard);
* dialog.open();
* </pre>
*
* During the call to <code>open</code>, the wizard dialog is presented to the user. When the user hits Finish, a project resource
* with the user-specified name is created, the dialog closes, and the call to <code>open</code> returns.
* </p>
*
* @noextend This class is not intended to be subclassed by clients.
*/
public class NewSyncProjectWizard extends Wizard implements INewWizard, IExecutableExtension {
/**
* The workbench.
*/
private IWorkbench workbench;
/**
* The current selection.
*/
protected IStructuredSelection selection;
/**
* The wizard id for creating new projects in the workspace.
*
* @since 3.4
*/
public static final String WIZARD_ID = "org.eclipse.ui.wizards.new.project"; //$NON-NLS-1$
private SyncMainWizardPage mainPage;
private WizardNewProjectReferencePage referencePage;
public enum WizardMode {
NEW, CONVERT
}
private WizardMode wizardMode = null;
// cache of newly-created project
private IProject newProject;
/**
* The config element which declares this wizard.
*/
private IConfigurationElement configElement;
private static String WINDOW_PROBLEMS_TITLE = Messages.NewProject_errorOpeningWindow;
/**
* Extension attribute name for final perspective.
*/
private static final String FINAL_PERSPECTIVE = "finalPerspective"; //$NON-NLS-1$
/**
* Extension attribute name for preferred perspectives.
*/
private static final String PREFERRED_PERSPECTIVES = "preferredPerspectives"; //$NON-NLS-1$
/**
* Creates a wizard for creating a new project resource in the workspace.
*/
public NewSyncProjectWizard() {
IDialogSettings workbenchSettings = IDEWorkbenchPlugin.getDefault().getDialogSettings();
IDialogSettings section = workbenchSettings.getSection("NewSyncProjectWizard");//$NON-NLS-1$
if (section == null) {
section = workbenchSettings.addNewSection("NewSyncProjectWizard");//$NON-NLS-1$
}
setDialogSettings(section);
SyncWizardDataCache.clearProperties();
}
/**
* Creates a wizard that operates according to the passed mode
*/
public NewSyncProjectWizard(WizardMode mode) {
this();
wizardMode = mode;
}
/**
* Returns the selection which was passed to <code>init</code>.
*
* @return the selection
*/
public IStructuredSelection getSelection() {
return selection;
}
/**
* Returns the workbench which was passed to <code>init</code>.
*
* @return the workbench
*/
public IWorkbench getWorkbench() {
return workbench;
}
/*
* (non-Javadoc) Method declared on IWizard.
*/
@Override
public void addPages() {
super.addPages();
// By default run as a new project wizard
if (wizardMode == null) {
wizardMode = WizardMode.NEW;
}
if (wizardMode == WizardMode.NEW) {
mainPage = SyncMainWizardPage.newProjectPage("basicNewProjectPage");//$NON-NLS-1$
mainPage.setTitle(Messages.NewProject_title);
mainPage.setDescription(Messages.NewProject_description);
} else if (wizardMode == WizardMode.CONVERT){
mainPage = SyncMainWizardPage.convertProjectPage("basicConvertProjectPage"); //$NON-NLS-1$
mainPage.setTitle(Messages.ConvertProject_title);
mainPage.setDescription(Messages.ConvertProject_description);
}
this.addPage(mainPage);
// only add page if there are already projects in the workspace
if (ResourcesPlugin.getWorkspace().getRoot().getProjects().length > 0) {
referencePage = new WizardNewProjectReferencePage("basicReferenceProjectPage");//$NON-NLS-1$
referencePage.setTitle(Messages.NewProject_referenceTitle);
referencePage.setDescription(Messages.NewProject_referenceDescription);
this.addPage(referencePage);
}
}
/**
* Creates a new project resource with the selected name.
* <p>
* In normal usage, this method is invoked after the user has pressed Finish on the wizard; the enablement of the Finish button
* implies that all controls on the pages currently contain valid values.
* </p>
* <p>
* Note that this wizard caches the new project once it has been successfully created; subsequent invocations of this method
* will answer the same project resource without attempting to create it again.
* </p>
*
* @return the created project resource, or <code>null</code> if the
* project was not created
*/
private boolean createNewProject() {
if (newProject != null) {
return true;
}
// get a project handle
final IProject newProjectHandle = mainPage.getProjectHandle();
// get a project descriptor
URI location = null;
if (!mainPage.useDefaults()) {
location = mainPage.getLocationURI();
}
IWorkspace workspace = ResourcesPlugin.getWorkspace();
final IProjectDescription description = workspace.newProjectDescription(newProjectHandle.getName());
description.setLocationURI(location);
// update the referenced project if provided
if (referencePage != null) {
IProject[] refProjects = referencePage.getReferencedProjects();
if (refProjects.length > 0) {
description.setReferencedProjects(refProjects);
}
}
// create the new project operation
IRunnableWithProgress op = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException {
CreateProjectOperation op = new CreateProjectOperation(description, Messages.NewProject_windowTitle);
try {
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=219901
// directly execute the operation so that the undo state is
// not preserved. Making this undoable resulted in too many
// accidental file deletions.
op.execute(monitor, WorkspaceUndoUtil.getUIInfoAdapter(getShell()));
} catch (ExecutionException e) {
throw new InvocationTargetException(e);
}
}
};
// run the new project creation operation
try {
getContainer().run(true, true, op);
} catch (InterruptedException e) {
return false;
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t instanceof ExecutionException && t.getCause() instanceof CoreException) {
CoreException cause = (CoreException) t.getCause();
StatusAdapter status;
if (cause.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
status = new StatusAdapter(StatusUtil.newStatus(IStatus.WARNING,
NLS.bind(Messages.NewProject_caseVariantExistsError, newProjectHandle.getName()), cause));
} else {
status = new StatusAdapter(StatusUtil.newStatus(cause.getStatus().getSeverity(),
Messages.NewProject_errorMessage, cause));
}
status.setProperty(StatusAdapter.TITLE_PROPERTY, Messages.NewProject_errorMessage);
StatusManager.getManager().handle(status, StatusManager.BLOCK);
} else {
StatusAdapter status = new StatusAdapter(new Status(IStatus.WARNING, IDEWorkbenchPlugin.IDE_WORKBENCH, 0, NLS.bind(
Messages.NewProject_internalError, t.getMessage()), t));
status.setProperty(StatusAdapter.TITLE_PROPERTY, Messages.NewProject_errorMessage);
StatusManager.getManager().handle(status, StatusManager.LOG | StatusManager.BLOCK);
}
return false;
}
newProject = newProjectHandle;
return true;
}
/**
* Returns the newly created project.
*
* @return the created project, or <code>null</code> if project not
* created
*/
public IProject getNewProject() {
return newProject;
}
/*
* (non-Javadoc) Method declared on IWorkbenchWizard.
*/
@Override
public void init(IWorkbench workbench, IStructuredSelection currentSelection) {
this.workbench = workbench;
this.selection = currentSelection;
initializeDefaultPageImageDescriptor();
setNeedsProgressMonitor(true);
// By default run as a new project wizard
if (wizardMode == null) {
wizardMode = WizardMode.NEW;
}
if (wizardMode == WizardMode.NEW) {
setWindowTitle(Messages.NewProject_windowTitle);
} else if (wizardMode == WizardMode.CONVERT) {
setWindowTitle(Messages.ConvertProject_windowTitle);
}
}
/*
* (non-Javadoc) Method declared on BasicNewResourceWizard.
*/
protected void initializeDefaultPageImageDescriptor() {
ImageDescriptor desc = IDEWorkbenchPlugin.getIDEImageDescriptor("wizban/newprj_wiz.png");//$NON-NLS-1$
setDefaultPageImageDescriptor(desc);
}
/*
* (non-Javadoc) Opens a new window with a particular perspective and input.
*/
private static void openInNewWindow(IPerspectiveDescriptor desc) {
// Open the page.
try {
PlatformUI.getWorkbench().openWorkbenchWindow(desc.getId(), ResourcesPlugin.getWorkspace().getRoot());
} catch (WorkbenchException e) {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
ErrorDialog.openError(window.getShell(), WINDOW_PROBLEMS_TITLE, e.getMessage(), e.getStatus());
}
}
}
/*
* (non-Javadoc) Method declared on IWizard.
*/
@Override
public boolean performFinish() {
// Create project if necessary
if (wizardMode == WizardMode.NEW) {
if (!createNewProject()) {
return false;
}
IWorkingSet[] workingSets = mainPage.getSelectedWorkingSets();
getWorkbench().getWorkingSetManager().addToWorkingSets(newProject, workingSets);
updatePerspective();
BasicNewResourceWizard.selectAndReveal(newProject, getWorkbench().getActiveWorkbenchWindow());
}
IProject project;
if (wizardMode == WizardMode.NEW) {
project = newProject;
} else {
project = mainPage.getProjectHandle();
}
// Add sync-specific elements to project
try {
ISynchronizeParticipant part = mainPage.getSynchronizeParticipant();
SyncManager.makeSyncProject(project, part.getSyncConfigName(), part.getServiceId(), part.getConnection(),
part.getLocation(), mainPage.getCustomFileFilter());
} catch (CoreException e) {
return false;
}
// Execute any wizard page extension code. All project setup and creation should be done before invoking extension.
ISynchronizeWizardExtension ext = mainPage.getExtension();
if (ext != null) {
ext.performFinish();
}
// Enable syncing
SyncManager.setSyncMode(project, SyncMode.ACTIVE);
// Force an initial sync
try {
SyncManager.sync(null, project, SyncFlag.BOTH, new CommonSyncExceptionHandler(false, true));
} catch (CoreException e) {
// This should never happen because only a blocking sync can throw a core exception.
RDTSyncUIPlugin.log(Messages.NewSyncProjectWizard_Unexpected_core_exception, e);
}
return true;
}
/*
* (non-Javadoc) Replaces the current perspective with the new one.
*/
private static void replaceCurrentPerspective(IPerspectiveDescriptor persp) {
// Get the active page.
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
return;
}
IWorkbenchPage page = window.getActivePage();
if (page == null) {
return;
}
// Set the perspective.
page.setPerspective(persp);
}
/**
* Stores the configuration element for the wizard. The config element will
* be used in <code>performFinish</code> to set the result perspective.
*/
@Override
public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
configElement = cfig;
}
/**
* Updates the perspective for the active page within the window.
*/
protected void updatePerspective() {
updatePerspective(configElement);
}
/**
* Updates the perspective based on the current settings in the
* Workbench/Perspectives preference page.
*
* Use the setting for the new perspective opening if we are set to open in
* a new perspective.
* <p>
* A new project wizard class will need to implement the <code>IExecutableExtension</code> interface so as to gain access to the
* wizard's <code>IConfigurationElement</code>. That is the configuration element to pass into this method.
* </p>
*
* @param configElement
* -
* the element we are updating with
*
* @see IPreferenceConstants#OPM_NEW_WINDOW
* @see IPreferenceConstants#OPM_ACTIVE_PAGE
* @see IWorkbenchPreferenceConstants#NO_NEW_PERSPECTIVE
*/
@SuppressWarnings("unchecked")
public static void updatePerspective(IConfigurationElement configElement) {
// Do not change perspective if the configuration element is
// not specified.
if (configElement == null) {
return;
}
// Retrieve the new project open perspective preference setting
String perspSetting = PrefUtil.getAPIPreferenceStore().getString(IDE.Preferences.PROJECT_OPEN_NEW_PERSPECTIVE);
String promptSetting = IDEWorkbenchPlugin.getDefault().getPreferenceStore()
.getString(IDEInternalPreferences.PROJECT_SWITCH_PERSP_MODE);
// Return if do not switch perspective setting and are not prompting
if (!(promptSetting.equals(MessageDialogWithToggle.PROMPT))
&& perspSetting.equals(IWorkbenchPreferenceConstants.NO_NEW_PERSPECTIVE)) {
return;
}
// Read the requested perspective id to be opened.
String finalPerspId = configElement.getAttribute(FINAL_PERSPECTIVE);
if (finalPerspId == null) {
return;
}
// Map perspective id to descriptor.
IPerspectiveRegistry reg = PlatformUI.getWorkbench().getPerspectiveRegistry();
// leave this code in - the perspective of a given project may map to
// activities other than those that the wizard itself maps to.
IPerspectiveDescriptor finalPersp = reg.findPerspectiveWithId(finalPerspId);
if (finalPersp != null && finalPersp instanceof IPluginContribution) {
IPluginContribution contribution = (IPluginContribution) finalPersp;
if (contribution.getPluginId() != null) {
IWorkbenchActivitySupport workbenchActivitySupport = PlatformUI.getWorkbench().getActivitySupport();
IActivityManager activityManager = workbenchActivitySupport.getActivityManager();
IIdentifier identifier = activityManager.getIdentifier(WorkbenchActivityHelper.createUnifiedId(contribution));
Set<String> idActivities = identifier.getActivityIds();
if (!idActivities.isEmpty()) {
Set<String> enabledIds = new HashSet<String>(activityManager.getEnabledActivityIds());
if (enabledIds.addAll(idActivities)) {
workbenchActivitySupport.setEnabledActivityIds(enabledIds);
}
}
}
} else {
IDEWorkbenchPlugin.log(NLS.bind(Messages.NewSyncProjectWizard_Unable_to_find_perspective, finalPerspId));
return;
}
// gather the preferred perspectives
// always consider the final perspective (and those derived from it)
// to be preferred
List<String> preferredPerspIds = new ArrayList<String>();
addPerspectiveAndDescendants(preferredPerspIds, finalPerspId);
String preferred = configElement.getAttribute(PREFERRED_PERSPECTIVES);
if (preferred != null) {
StringTokenizer tok = new StringTokenizer(preferred, " \t\n\r\f,"); //$NON-NLS-1$
while (tok.hasMoreTokens()) {
addPerspectiveAndDescendants(preferredPerspIds, tok.nextToken());
}
}
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage page = window.getActivePage();
if (page != null) {
IPerspectiveDescriptor currentPersp = page.getPerspective();
// don't switch if the current perspective is a preferred
// perspective
if (currentPersp != null && preferredPerspIds.contains(currentPersp.getId())) {
return;
}
}
// prompt the user to switch
if (!confirmPerspectiveSwitch(window, finalPersp)) {
return;
}
}
int workbenchPerspectiveSetting = WorkbenchPlugin.getDefault().getPreferenceStore()
.getInt(IPreferenceConstants.OPEN_PERSP_MODE);
// open perspective in new window setting
if (workbenchPerspectiveSetting == IPreferenceConstants.OPM_NEW_WINDOW) {
openInNewWindow(finalPersp);
return;
}
// replace active perspective setting otherwise
replaceCurrentPerspective(finalPersp);
}
/**
* Adds to the list all perspective IDs in the Workbench who's original ID
* matches the given ID.
*
* @param perspectiveIds
* the list of perspective IDs to supplement.
* @param id
* the id to query.
* @since 3.0
*/
private static void addPerspectiveAndDescendants(List<String> perspectiveIds, String id) {
IPerspectiveRegistry registry = PlatformUI.getWorkbench().getPerspectiveRegistry();
IPerspectiveDescriptor[] perspectives = registry.getPerspectives();
for (IPerspectiveDescriptor perspective : perspectives) {
// @issue illegal ref to workbench internal class;
// consider adding getOriginalId() as API on IPerspectiveDescriptor
PerspectiveDescriptor descriptor = ((PerspectiveDescriptor) perspective);
if (descriptor.getOriginalId().equals(id)) {
perspectiveIds.add(descriptor.getId());
}
}
}
/**
* Prompts the user for whether to switch perspectives.
*
* @param window
* The workbench window in which to switch perspectives; must not
* be <code>null</code>
* @param finalPersp
* The perspective to switch to; must not be <code>null</code>.
*
* @return <code>true</code> if it's OK to switch, <code>false</code> otherwise
*/
private static boolean confirmPerspectiveSwitch(IWorkbenchWindow window, IPerspectiveDescriptor finalPersp) {
IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
String pspm = store.getString(IDEInternalPreferences.PROJECT_SWITCH_PERSP_MODE);
if (!IDEInternalPreferences.PSPM_PROMPT.equals(pspm)) {
// Return whether or not we should always switch
return IDEInternalPreferences.PSPM_ALWAYS.equals(pspm);
}
String desc = finalPersp.getDescription();
String message;
if (desc == null || desc.length() == 0) {
message = NLS.bind(Messages.NewProject_perspSwitchMessage, finalPersp.getLabel());
} else {
message = NLS.bind(Messages.NewProject_perspSwitchMessageWithDesc, new String[] { finalPersp.getLabel(), desc });
}
MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(window.getShell(),
Messages.NewProject_perspSwitchTitle, message, null /* use the default message for the toggle */, false /*
* toggle is
* initially
* unchecked
*/, store,
IDEInternalPreferences.PROJECT_SWITCH_PERSP_MODE);
int result = dialog.getReturnCode();
// If we are not going to prompt anymore propogate the choice.
if (dialog.getToggleState()) {
String preferenceValue;
if (result == IDialogConstants.YES_ID) {
// Doesn't matter if it is replace or new window
// as we are going to use the open perspective setting
preferenceValue = IWorkbenchPreferenceConstants.OPEN_PERSPECTIVE_REPLACE;
} else {
preferenceValue = IWorkbenchPreferenceConstants.NO_NEW_PERSPECTIVE;
}
// update PROJECT_OPEN_NEW_PERSPECTIVE to correspond
PrefUtil.getAPIPreferenceStore().setValue(IDE.Preferences.PROJECT_OPEN_NEW_PERSPECTIVE, preferenceValue);
}
return result == IDialogConstants.YES_ID;
}
}