blob: 4977be487b0cae8a7b5a5d0658cf81233ed8749d [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ui.wizards;
import java.util.Iterator;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImSet;
import org.eclipse.statet.ecommons.ui.SharedMessages;
import org.eclipse.statet.ecommons.ui.components.StatusInfo;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.workbench.ContainerSelectionComposite;
import org.eclipse.statet.ecommons.ui.workbench.ContainerSelectionComposite.ContainerFilter;
/**
* Standard main page for a wizard that creates a file resource.
* <p>
* This page may be used by clients as-is; it may be also be subclassed to suit.
* </p>
* <p>
* Subclasses may override
* <ul>
* <li><code>getInitialContents</code></li>
* <li><code>getNewFileLabel</code></li>
* </ul>
* </p>
* <p>
* Subclasses may extend
* <ul>
* <li><code>handleEvent</code></li>
* </ul>
* </p>
*/
public abstract class NewElementWizardPage extends WizardPage {
protected static class ProjectNatureContainerFilter extends ContainerFilter {
private final ImSet<String> natureIds;
public ProjectNatureContainerFilter(final ImSet<String> natureIds) {
this.natureIds= natureIds;
}
public ProjectNatureContainerFilter(final String natureId) {
this.natureIds= ImCollections.newSet(natureId);
}
@Override
public boolean select(final IContainer container) {
try {
final IProject project= container.getProject();
for (final String natureId : this.natureIds) {
if (!project.hasNature(natureId)) {
return false;
}
}
return true;
}
catch (final CoreException e) {
return false;
}
}
}
public class ResourceGroup implements Listener {
private static final int SIZING_CONTAINER_GROUP_HEIGHT= 250;
private static final String DIALOGSETTING_ENABLEFILTER= "org.eclipse.statet.base.NewElementWizard.ContainerFilter"; //$NON-NLS-1$
private final String resourceNameDefaultSuffix;
private final ContainerFilter containerFilter;
private ContainerSelectionComposite containerGroup;
private Text resourceNameControl;
private IPath containerFullPath;
private String resourceName;
private boolean resourceNameEdited;
public ResourceGroup(final String defaultResourceNameExtension,
final ContainerFilter containerFilter) {
this.resourceNameDefaultSuffix= defaultResourceNameExtension;
this.containerFilter= containerFilter;
}
public void createGroup(final Composite parent) {
final Composite composite= new Composite(parent, SWT.NONE);
composite.setLayout(LayoutUtils.newCompositeGrid(2));
// resource and container group
this.containerGroup= new ContainerSelectionComposite(
composite, true, false,
null, SIZING_CONTAINER_GROUP_HEIGHT );
this.containerGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
this.containerGroup.setListener(this);
boolean enableFilter= true;
if (getDialogSettings().get(DIALOGSETTING_ENABLEFILTER) != null) {
enableFilter= getDialogSettings().getBoolean(DIALOGSETTING_ENABLEFILTER);
}
this.containerGroup.setToggleFilter(this.containerFilter, enableFilter);
{ final Label label= new Label(composite, SWT.LEFT);
label.setText(getNewFileLabel());
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
final Text input= new Text(composite, SWT.SINGLE | SWT.BORDER);
input.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.resourceNameControl= input;
this.resourceNameControl.addListener(SWT.Modify, this);
}
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
initFields();
}
/**
* Returns the label to display in the file name specification visual
* component group.
* <p>
* Subclasses may reimplement.
* </p>
*
* @return the label to display in the file name specification visual
* component group
*/
protected String getNewFileLabel() {
return LTKWizardsMessages.ResourceGroup_NewFile_label;
}
/**
* Sets the initial contents of the container name entry field, based upon
* either a previously-specified initial value or the ability to determine
* such a value.
*/
protected void initFields() {
IPath path= null;
if (this.containerFullPath != null) {
path= this.containerFullPath;
}
else {
final Iterator<?> it= NewElementWizardPage.this.resourceSelection.iterator();
if (it.hasNext()) {
final Object object= it.next();
IResource selectedResource= null;
if (object instanceof IResource) {
selectedResource= (IResource) object;
} else if (object instanceof IAdaptable) {
selectedResource= ((IAdaptable) object).getAdapter(IResource.class);
}
if (selectedResource != null) {
if (selectedResource.getType() == IResource.FILE) {
selectedResource= selectedResource.getParent();
}
if (selectedResource.isAccessible()) {
path= selectedResource.getFullPath();
}
}
}
}
if (path != null) {
this.containerGroup.selectContainer(path);
}
if (this.resourceName != null) {
this.resourceNameControl.setText(this.resourceName);
}
}
@Override
public void handleEvent(final Event event) {
this.containerFullPath= this.containerGroup.getContainerFullPath();
final String name= this.resourceNameControl.getText();
if (!this.resourceNameEdited && event.widget == this.resourceNameControl
&& name.length() > 0 && !name.equals(this.resourceName)) {
this.resourceNameEdited= true;
}
this.resourceName= name;
validatePage();
}
// /**
// * Sets the value of this page's container name field, or stores
// * it for future use if this page's controls do not exist yet.
// *
// * @param path the full path to the container
// */
// public void setContainerFullPath(IPath path) {
// if (fResourceGroup != null)
// fResourceGroup.setContainerFullPath(path); // update resourceName?
// else
// fContainerFullPath= path;
// }
//
// /**
// * Sets the value of this page's file name field, or stores
// * it for future use if this page's controls do not exist yet.
// *
// * @param value new file name
// */
// public void setFileName(String value) {
// if (fResourceGroup != null)
// fResourceGroup.setResource(value); // update resourceName?
// else
// resourceName= value;
// }
/**
* Returns the current full path of the containing resource as entered or
* selected by the user, or its anticipated initial value.
*
* @return the container's full path, anticipated initial value,
* or <code>null</code> if no path is known
*/
public IPath getContainerFullPath() {
return this.containerFullPath;
}
/**
* Returns the current file name as entered by the user, or its anticipated
* initial value.
*
* @return the file name, its anticipated initial value, or <code>null</code>
* if no file name is known
*/
public String getResourceName() {
String name= this.resourceName;
if (!name.endsWith(this.resourceNameDefaultSuffix)) {
name += this.resourceNameDefaultSuffix;
}
return name;
}
public void setFocus() {
this.resourceNameControl.setFocus();
}
/**
*
* @return true, if ok
*/
public IStatus validate() {
// don't attempt to validate controls until they have been created
if (this.containerGroup == null) {
return null;
}
final IStatus containerSelectionStatus= ContainerSelectionComposite.validate(this.containerFullPath);
if (containerSelectionStatus.matches(IStatus.ERROR)) {
return containerSelectionStatus;
}
final IStatus resourceNameStatus= validateResourceName(this.resourceName);
if (resourceNameStatus == null || resourceNameStatus.matches(IStatus.ERROR)) {
return resourceNameStatus;
}
final IPath path= this.containerGroup.getContainerFullPath().append(getResourceName());
// no warnings
return validateFullResourcePath(path);
}
protected IStatus validateResourceName(final String resourceName) {
if (resourceName == null || resourceName.trim().isEmpty()) {
if (this.resourceNameEdited) {
return new StatusInfo(IStatus.ERROR, NLS.bind(
LTKWizardsMessages.ResourceGroup_error_EmptyName,
SharedMessages.Resources_File));
}
else {
return null;
}
}
if (!(new Path("")).isValidSegment(resourceName)) {
return new StatusInfo(IStatus.ERROR, NLS.bind(
LTKWizardsMessages.ResourceGroup_error_InvalidFilename,
resourceName));
}
return new StatusInfo();
}
/**
* Returns a <code>boolean</code> indicating whether the specified resource
* path represents a valid new resource in the workbench. An error message
* is stored for future reference if the path does not represent a valid
* new resource path.
*
* @param resourcePath the path to validate
* @return IStatus indicating validity of the resource path
*/
protected IStatus validateFullResourcePath(final IPath resourcePath) {
final IWorkspace workspace= ResourcesPlugin.getWorkspace();
final IStatus result= workspace.validatePath(resourcePath.toString(), IResource.FILE);
if (!result.isOK()) {
return result;
}
if ( // !allowExistingResources &&
workspace.getRoot().getFolder(resourcePath).exists()
|| workspace.getRoot().getFile(resourcePath).exists()) {
return new StatusInfo(IStatus.ERROR,
LTKWizardsMessages.ResourceGroup_error_ResourceExists );
}
return new StatusInfo();
}
public void saveSettings() {
getDialogSettings().put(DIALOGSETTING_ENABLEFILTER, this.containerGroup.getToggleFilterSetting());
}
}
/** the current resource selection */
protected IStructuredSelection resourceSelection;
/**
* Creates a new file creation wizard page. If the initial resource selection
* contains exactly one container resource then it will be used as the default
* container resource.
*
* @param pageName the name of the page
* @param selection the current resource selection
*/
public NewElementWizardPage(final String pageName, final IStructuredSelection selection) {
super(pageName);
setPageComplete(false);
this.resourceSelection= selection;
}
@Override
public void createControl(final Composite parent) {
initializeDialogUnits(parent);
final Composite composite= new Composite(parent, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
composite.setLayout(LayoutUtils.newContentGrid());
createContents(composite);
// validatePage();
// Show description on opening
setErrorMessage(null);
setMessage(null);
setControl(composite);
}
/**
* Subclasses should ADDHELP
*
* @param layouter
*/
protected abstract void createContents(final Composite parent);
/**
* Returns whether this page's controls currently all contain valid values.
*
* @return <code>true</code> if all controls are valid, and
* <code>false</code> if at least one is invalid
*/
protected void validatePage() {
updateStatus(new StatusInfo());
}
/**
* Updates the status line and the OK button according to the given status
*
* @param status status to apply
*/
protected void updateStatus(IStatus status) {
if (status != null) {
setPageComplete(!status.matches(IStatus.ERROR));
}
else {
setPageComplete(false);
status= new StatusInfo();
}
final Control control= getControl();
if (control != null && control.isVisible()) {
StatusInfo.applyToStatusLine(this, status);
}
}
}