blob: 682be3c35298aa1a46a02e4f9e369b41276c19ce [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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
*******************************************************************************/
package org.eclipse.ui.dialogs;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.internal.ide.DialogUtil;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.dialogs.ResourceTreeAndListGroup;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
/**
* Abstract superclass for a typical export wizard's main page.
* <p>
* Clients may subclass this page to inherit its common destination resource
* selection facilities.
* </p>
* <p>
* Subclasses must implement
* <ul>
* <li><code>createDestinationGroup</code></li>
* </ul>
* </p>
* <p>
* Subclasses may override
* <ul>
* <li><code>allowNewContainerName</code></li>
* </ul>
* </p>
* <p>
* Subclasses may extend
* <ul>
* <li><code>handleEvent</code></li>
* <li><code>internalSaveWidgetValues</code></li>
* <li><code>updateWidgetEnablements</code></li>
* </ul>
* </p>
*/
public abstract class WizardExportResourcesPage extends WizardDataTransferPage {
private IStructuredSelection initialResourceSelection;
private List selectedTypes = new ArrayList();
// widgets
private ResourceTreeAndListGroup resourceGroup;
private final static String SELECT_TYPES_TITLE = IDEWorkbenchMessages.WizardTransferPage_selectTypes;
private final static String SELECT_ALL_TITLE = IDEWorkbenchMessages.WizardTransferPage_selectAll;
private final static String DESELECT_ALL_TITLE = IDEWorkbenchMessages.WizardTransferPage_deselectAll;
/**
* Creates an export wizard page. If the current resource selection
* is not empty then it will be used as the initial collection of resources
* selected for export.
*
* @param pageName the name of the page
* @param selection the current resource selection
*/
protected WizardExportResourcesPage(String pageName,
IStructuredSelection selection) {
super(pageName);
this.initialResourceSelection = selection;
}
/**
* The <code>addToHierarchyToCheckedStore</code> implementation of this
* <code>WizardDataTransferPage</code> method returns <code>false</code>.
* Subclasses may override this method.
*/
protected boolean allowNewContainerName() {
return false;
}
/**
* Creates a new button with the given id.
* <p>
* The <code>Dialog</code> implementation of this framework method
* creates a standard push button, registers for selection events
* including button presses and registers
* default buttons with its shell.
* The button id is stored as the buttons client data.
* Note that the parent's layout is assumed to be a GridLayout and
* the number of columns in this layout is incremented.
* Subclasses may override.
* </p>
*
* @param parent the parent composite
* @param id the id of the button (see
* <code>IDialogConstants.*_ID</code> constants
* for standard dialog button ids)
* @param label the label from the button
* @param defaultButton <code>true</code> if the button is to be the
* default button, and <code>false</code> otherwise
*/
protected Button createButton(Composite parent, int id, String label,
boolean defaultButton) {
// increment the number of columns in the button bar
((GridLayout) parent.getLayout()).numColumns++;
Button button = new Button(parent, SWT.PUSH);
GridData buttonData = new GridData(GridData.FILL_HORIZONTAL);
button.setLayoutData(buttonData);
button.setData(new Integer(id));
button.setText(label);
button.setFont(parent.getFont());
if (defaultButton) {
Shell shell = parent.getShell();
if (shell != null) {
shell.setDefaultButton(button);
}
button.setFocus();
}
button.setFont(parent.getFont());
setButtonLayoutData(button);
return button;
}
/**
* Creates the buttons for selecting specific types or selecting all or none of the
* elements.
*
* @param parent the parent control
*/
protected final void createButtonsGroup(Composite parent) {
Font font = parent.getFont();
// top level group
Composite buttonComposite = new Composite(parent, SWT.NONE);
buttonComposite.setFont(parent.getFont());
GridLayout layout = new GridLayout();
layout.numColumns = 3;
layout.makeColumnsEqualWidth = true;
buttonComposite.setLayout(layout);
buttonComposite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
| GridData.HORIZONTAL_ALIGN_FILL));
// types edit button
Button selectTypesButton = createButton(buttonComposite,
IDialogConstants.SELECT_TYPES_ID, SELECT_TYPES_TITLE, false);
SelectionListener listener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
handleTypesEditButtonPressed();
}
};
selectTypesButton.addSelectionListener(listener);
selectTypesButton.setFont(font);
setButtonLayoutData(selectTypesButton);
Button selectButton = createButton(buttonComposite,
IDialogConstants.SELECT_ALL_ID, SELECT_ALL_TITLE, false);
listener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
resourceGroup.setAllSelections(true);
}
};
selectButton.addSelectionListener(listener);
selectButton.setFont(font);
setButtonLayoutData(selectButton);
Button deselectButton = createButton(buttonComposite,
IDialogConstants.DESELECT_ALL_ID, DESELECT_ALL_TITLE, false);
listener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
resourceGroup.setAllSelections(false);
}
};
deselectButton.addSelectionListener(listener);
deselectButton.setFont(font);
setButtonLayoutData(deselectButton);
}
/** (non-Javadoc)
* Method declared on IDialogPage.
*/
public void createControl(Composite parent) {
initializeDialogUnits(parent);
Composite composite = new Composite(parent, SWT.NULL);
composite.setLayout(new GridLayout());
composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
| GridData.HORIZONTAL_ALIGN_FILL));
composite.setFont(parent.getFont());
createResourcesGroup(composite);
createButtonsGroup(composite);
createDestinationGroup(composite);
createOptionsGroup(composite);
restoreResourceSpecificationWidgetValues(); // ie.- local
restoreWidgetValues(); // ie.- subclass hook
if (initialResourceSelection != null)
setupBasedOnInitialSelections();
updateWidgetEnablements();
setPageComplete(determinePageCompletion());
setErrorMessage(null); // should not initially have error message
setControl(composite);
}
/**
* Creates the export destination specification visual components.
* <p>
* Subclasses must implement this method.
* </p>
*
* @param parent the parent control
*/
protected abstract void createDestinationGroup(Composite parent);
/**
* Creates the checkbox tree and list for selecting resources.
*
* @param parent the parent control
*/
protected final void createResourcesGroup(Composite parent) {
//create the input element, which has the root resource
//as its only child
List input = new ArrayList();
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
.getProjects();
for (int i = 0; i < projects.length; i++) {
if (projects[i].isOpen())
input.add(projects[i]);
}
this.resourceGroup = new ResourceTreeAndListGroup(parent, input,
getResourceProvider(IResource.FOLDER | IResource.PROJECT),
WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider(),
getResourceProvider(IResource.FILE), WorkbenchLabelProvider
.getDecoratingWorkbenchLabelProvider(), SWT.NONE,
DialogUtil.inRegularFontMode(parent));
ICheckStateListener listener = new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent event) {
updateWidgetEnablements();
}
};
this.resourceGroup.addCheckStateListener(listener);
}
/*
* @see WizardDataTransferPage.getErrorDialogTitle()
*/
protected String getErrorDialogTitle() {
return IDEWorkbenchMessages.WizardExportPage_errorDialogTitle;
}
/**
* Obsolete method. This was implemented to handle the case where ensureLocal()
* needed to be called but it doesn't use it any longer.
*
* @deprecated Only retained for backwards compatibility.
*/
protected boolean ensureResourcesLocal(List resources) {
return true;
}
/**
* Returns a new subcollection containing only those resources which are not
* local.
*
* @param originalList the original list of resources (element type:
* <code>IResource</code>)
* @return the new list of non-local resources (element type:
* <code>IResource</code>)
*/
protected List extractNonLocalResources(List originalList) {
Vector result = new Vector(originalList.size());
Iterator resourcesEnum = originalList.iterator();
while (resourcesEnum.hasNext()) {
IResource currentResource = (IResource) resourcesEnum.next();
if (!currentResource.isLocal(IResource.DEPTH_ZERO))
result.addElement(currentResource);
}
return result;
}
/**
* Returns a content provider for <code>IResource</code>s that returns
* only children of the given resource type.
*/
private ITreeContentProvider getResourceProvider(final int resourceType) {
return new WorkbenchContentProvider() {
public Object[] getChildren(Object o) {
if (o instanceof IContainer) {
IResource[] members = null;
try {
members = ((IContainer) o).members();
} catch (CoreException e) {
//just return an empty set of children
return new Object[0];
}
//filter out the desired resource types
ArrayList results = new ArrayList();
for (int i = 0; i < members.length; i++) {
//And the test bits with the resource types to see if they are what we want
if ((members[i].getType() & resourceType) > 0) {
results.add(members[i]);
}
}
return results.toArray();
}
//input element case
if (o instanceof ArrayList) {
return ((ArrayList) o).toArray();
}
return new Object[0];
}
};
}
/**
* Returns this page's collection of currently-specified resources to be
* exported. This is the primary resource selection facility accessor for
* subclasses.
*
* @return a collection of resources currently selected
* for export (element type: <code>IResource</code>)
*/
protected List getSelectedResources() {
Iterator resourcesToExportIterator = this
.getSelectedResourcesIterator();
List resourcesToExport = new ArrayList();
while (resourcesToExportIterator.hasNext())
resourcesToExport.add(resourcesToExportIterator.next());
return resourcesToExport;
}
/**
* Returns this page's collection of currently-specified resources to be
* exported. This is the primary resource selection facility accessor for
* subclasses.
*
* @return an iterator over the collection of resources currently selected
* for export (element type: <code>IResource</code>). This will include
* white checked folders and individually checked files.
*/
protected Iterator getSelectedResourcesIterator() {
return this.resourceGroup.getAllCheckedListItems().iterator();
}
/**
* Returns the resource extensions currently specified to be exported.
*
* @return the resource extensions currently specified to be exported (element
* type: <code>String</code>)
*/
protected List getTypesToExport() {
return selectedTypes;
}
/**
* Returns this page's collection of currently-specified resources to be
* exported. This returns both folders and files - for just the files use
* getSelectedResources.
*
* @return a collection of resources currently selected
* for export (element type: <code>IResource</code>)
*/
protected List getWhiteCheckedResources() {
return this.resourceGroup.getAllWhiteCheckedItems();
}
/**
* Queries the user for the types of resources to be exported and selects
* them in the checkbox group.
*/
protected void handleTypesEditButtonPressed() {
Object[] newSelectedTypes = queryResourceTypesToExport();
if (newSelectedTypes != null) { // ie.- did not press Cancel
this.selectedTypes = new ArrayList(newSelectedTypes.length);
for (int i = 0; i < newSelectedTypes.length; i++) {
this.selectedTypes.add(newSelectedTypes[i]);
}
setupSelectionsBasedOnSelectedTypes();
}
}
/**
* Returns whether the extension of the given resource name is an extension that
* has been specified for export by the user.
*
* @param resourceName the resource name
* @return <code>true</code> if the resource name is suitable for export based
* upon its extension
*/
protected boolean hasExportableExtension(String resourceName) {
if (selectedTypes == null) // ie.- all extensions are acceptable
return true;
int separatorIndex = resourceName.lastIndexOf("."); //$NON-NLS-1$
if (separatorIndex == -1)
return false;
String extension = resourceName.substring(separatorIndex + 1);
Iterator it = selectedTypes.iterator();
while (it.hasNext()) {
if (extension.equalsIgnoreCase((String) it.next()))
return true;
}
return false;
}
/**
* Persists additional setting that are to be restored in the next instance of
* this page.
* <p>
* The <code>WizardImportPage</code> implementation of this method does
* nothing. Subclasses may extend to persist additional settings.
* </p>
*/
protected void internalSaveWidgetValues() {
}
/**
* Queries the user for the resource types that are to be exported and returns
* these types as an array.
*
* @return the resource types selected for export (element type:
* <code>String</code>), or <code>null</code> if the user canceled the
* selection
*/
protected Object[] queryResourceTypesToExport() {
TypeFilteringDialog dialog = new TypeFilteringDialog(getContainer()
.getShell(), getTypesToExport());
dialog.open();
return dialog.getResult();
}
/**
* Restores resource specification control settings that were persisted
* in the previous instance of this page. Subclasses wishing to restore
* persisted values for their controls may extend.
*/
protected void restoreResourceSpecificationWidgetValues() {
}
/**
* Persists resource specification control setting that are to be restored
* in the next instance of this page. Subclasses wishing to persist additional
* setting for their controls should extend hook method
* <code>internalSaveWidgetValues</code>.
*/
protected void saveWidgetValues() {
// allow subclasses to save values
internalSaveWidgetValues();
}
/**
* Set the initial selections in the resource group.
*/
protected void setupBasedOnInitialSelections() {
Iterator it = this.initialResourceSelection.iterator();
while (it.hasNext()) {
IResource currentResource = (IResource) it.next();
if (currentResource.getType() == IResource.FILE)
this.resourceGroup.initialCheckListItem(currentResource);
else
this.resourceGroup.initialCheckTreeItem(currentResource);
}
}
/**
* Update the tree to only select those elements that match the selected types
*/
private void setupSelectionsBasedOnSelectedTypes() {
Runnable runnable = new Runnable() {
public void run() {
Map selectionMap = new Hashtable();
//Only get the white selected ones
Iterator resourceIterator = resourceGroup
.getAllWhiteCheckedItems().iterator();
while (resourceIterator.hasNext()) {
//handle the files here - white checked containers require recursion
IResource resource = (IResource) resourceIterator.next();
if (resource.getType() == IResource.FILE) {
if (hasExportableExtension(resource.getName())) {
List resourceList = new ArrayList();
IContainer parent = resource.getParent();
if (selectionMap.containsKey(parent))
resourceList = (List) selectionMap.get(parent);
resourceList.add(resource);
selectionMap.put(parent, resourceList);
}
} else
setupSelectionsBasedOnSelectedTypes(selectionMap,
(IContainer) resource);
}
resourceGroup.updateSelections(selectionMap);
}
};
BusyIndicator.showWhile(getShell().getDisplay(), runnable);
}
/**
* Set up the selection values for the resources and put them in the selectionMap.
* If a resource is a file see if it matches one of the selected extensions. If not
* then check the children.
*/
private void setupSelectionsBasedOnSelectedTypes(Map selectionMap,
IContainer parent) {
List selections = new ArrayList();
IResource[] resources;
boolean hasFiles = false;
try {
resources = parent.members();
} catch (CoreException exception) {
//Just return if we can't get any info
return;
}
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
if (resource.getType() == IResource.FILE) {
if (hasExportableExtension(resource.getName())) {
hasFiles = true;
selections.add(resource);
}
} else {
setupSelectionsBasedOnSelectedTypes(selectionMap,
(IContainer) resource);
}
}
//Only add it to the list if there are files in this folder
if (hasFiles)
selectionMap.put(parent, selections);
}
/**
* Save any editors that the user wants to save before export.
* @return boolean if the save was successful.
*/
protected boolean saveDirtyEditors() {
return IDEWorkbenchPlugin.getDefault().getWorkbench().saveAllEditors(
true);
}
/**
* Check if widgets are enabled or disabled by a change in the dialog.
*/
protected void updateWidgetEnablements() {
boolean pageComplete = determinePageCompletion();
setPageComplete(pageComplete);
if (pageComplete)
setMessage(null);
super.updateWidgetEnablements();
}
}