| /*=============================================================================# |
| # 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); |
| } |
| } |
| |
| } |