| /******************************************************************************* |
| * Copyright (c) 2000, 2018 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.ui.wizards; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| 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.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.IScriptFolder; |
| import org.eclipse.dltk.core.IScriptModel; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.ScriptModelUtil; |
| import org.eclipse.dltk.internal.core.ExternalScriptFolder; |
| import org.eclipse.dltk.internal.corext.util.Messages; |
| import org.eclipse.dltk.internal.ui.StandardModelElementContentProvider; |
| import org.eclipse.dltk.internal.ui.wizards.NewWizardMessages; |
| import org.eclipse.dltk.internal.ui.wizards.TypedViewerFilter; |
| import org.eclipse.dltk.internal.ui.wizards.dialogfields.DialogField; |
| import org.eclipse.dltk.internal.ui.wizards.dialogfields.IDialogFieldListener; |
| import org.eclipse.dltk.internal.ui.wizards.dialogfields.IStringButtonAdapter; |
| import org.eclipse.dltk.internal.ui.wizards.dialogfields.LayoutUtil; |
| import org.eclipse.dltk.internal.ui.wizards.dialogfields.StringButtonDialogField; |
| import org.eclipse.dltk.ui.DLTKUIPlugin; |
| import org.eclipse.dltk.ui.ModelElementLabelProvider; |
| import org.eclipse.dltk.ui.ModelElementSorter; |
| import org.eclipse.dltk.ui.dialogs.StatusInfo; |
| import org.eclipse.dltk.ui.viewsupport.IViewPartInputProvider; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerFilter; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.dialogs.ElementTreeSelectionDialog; |
| import org.eclipse.ui.dialogs.ISelectionStatusValidator; |
| import org.eclipse.ui.views.contentoutline.ContentOutline; |
| |
| /** |
| * Wizard page that acts as a base class for wizard pages that create new Script |
| * elements. The class provides a input field for source folders (called |
| * container in this class) and API to validate the enter source folder name. |
| * |
| * <p> |
| * Clients may subclass. |
| * </p> |
| * |
| * |
| */ |
| public abstract class NewContainerWizardPage extends NewElementWizardPage { |
| /** Id of the container field */ |
| protected static final String CONTAINER = "NewContainerWizardPage.container"; //$NON-NLS-1$ |
| |
| /** |
| * The status of the last validation of the container. |
| */ |
| protected IStatus containerStatus; |
| |
| private StringButtonDialogField containerDialogField; |
| |
| // script folder corresponding to the input type (can be null) |
| private IScriptFolder currRoot; |
| |
| private IWorkspaceRoot workspaceRoot; |
| |
| /** |
| * Filter used in {@link NewContainerWizardPage#chooseContainer()} to show |
| * only selectable elements. |
| */ |
| protected static class ContainerViewerFilter extends TypedViewerFilter { |
| public ContainerViewerFilter() { |
| this(new Class[] { IScriptModel.class, IScriptFolder.class, |
| IScriptProject.class, IProjectFragment.class }); |
| } |
| |
| public ContainerViewerFilter(Class<?>[] acceptedTypes) { |
| super(acceptedTypes); |
| } |
| |
| @Override |
| public boolean select(Viewer viewer, Object parent, Object element) { |
| if (element instanceof IProjectFragment) { |
| try { |
| IProjectFragment fragment = (IProjectFragment) element; |
| if (fragment.getKind() != IProjectFragment.K_SOURCE |
| || fragment.isExternal()) |
| return false; |
| } catch (ModelException e) { |
| return false; |
| } |
| return true; |
| } |
| return super.select(viewer, parent, element); |
| } |
| } |
| |
| private class ContainerFieldAdapter |
| implements IStringButtonAdapter, IDialogFieldListener { |
| @Override |
| public void changeControlPressed(DialogField field) { |
| containerChangeControlPressed(field); |
| } |
| |
| @Override |
| public void dialogFieldChanged(DialogField field) { |
| containerDialogFieldChanged(field); |
| } |
| } |
| |
| /** |
| * Create a new <code>NewContainerWizardPage</code> |
| * |
| * @param name |
| * the wizard page's name |
| */ |
| public NewContainerWizardPage(String name) { |
| super(name); |
| workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| ContainerFieldAdapter adapter = new ContainerFieldAdapter(); |
| containerDialogField = new StringButtonDialogField(adapter); |
| containerDialogField.setDialogFieldListener(adapter); |
| containerDialogField.setLabelText(getContainerLabel()); |
| containerDialogField.setButtonLabel( |
| NewWizardMessages.NewContainerWizardPage_container_button); |
| containerStatus = new StatusInfo(); |
| currRoot = null; |
| } |
| |
| /** |
| * Returns the label that is used for the container input field. |
| * |
| * @return the label that is used for the container input field. |
| * |
| */ |
| protected String getContainerLabel() { |
| return NewWizardMessages.NewContainerWizardPage_container_label; |
| } |
| |
| /** |
| * Initializes the source folder field with a valid package fragment root. |
| * The package fragment root is computed from the given Script element. |
| * |
| * @param elem |
| * the Script element used to compute the initial package |
| * fragment root used as the source folder |
| */ |
| protected void initContainerPage(IModelElement elem) { |
| IScriptFolder initRoot = null; |
| if (elem != null) { |
| initRoot = (IScriptFolder) elem |
| .getAncestor(IModelElement.SCRIPT_FOLDER); |
| if (initRoot instanceof ExternalScriptFolder) |
| initRoot = null; |
| // TODO: I think this piece of code is a mess, please fix it |
| try { |
| if (initRoot == null) { |
| IProjectFragment fragment = ScriptModelUtil |
| .getProjectFragment(elem); |
| if (fragment != null |
| && fragment.getKind() == IProjectFragment.K_SOURCE |
| && !fragment.isExternal()) |
| initRoot = fragment.getScriptFolder(""); //$NON-NLS-1$ |
| |
| if (initRoot == null) { |
| IScriptProject project = elem.getScriptProject(); |
| if (project != null) { |
| initRoot = null; |
| if (project.exists()) { |
| IProjectFragment[] roots = project |
| .getProjectFragments(); |
| for (int i = 0; i < roots.length; i++) { |
| if (roots[i] |
| .getKind() == IProjectFragment.K_SOURCE) { |
| initRoot = roots[i].getScriptFolder(""); //$NON-NLS-1$ |
| break; |
| } |
| } |
| } |
| if (initRoot == null) { |
| initRoot = project |
| .getProjectFragment( |
| project.getResource()) |
| .getScriptFolder(""); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| } catch (ModelException e) { |
| DLTKUIPlugin.log(e); |
| } |
| } |
| setScriptFolder(initRoot, true); |
| handleFieldChanged(CONTAINER); |
| } |
| |
| /** |
| * Utility method to inspect a selection to find a Script element. |
| * |
| * @param selection |
| * the selection to be inspected |
| * @return a Script element to be used as the initial selection, or |
| * <code>null</code>, if no Script element exists in the given |
| * selection |
| */ |
| protected IModelElement getInitialScriptElement( |
| IStructuredSelection selection) { |
| IModelElement scriptElement = null; |
| |
| // Check selection |
| if (selection != null && !selection.isEmpty()) { |
| Object selectedElement = selection.getFirstElement(); |
| // Check for adapters |
| if (selectedElement instanceof IAdaptable) { |
| IAdaptable adaptable = (IAdaptable) selectedElement; |
| scriptElement = adaptable.getAdapter(IModelElement.class); |
| if (scriptElement != null && scriptElement.isReadOnly()) { |
| scriptElement = scriptElement.getScriptProject(); |
| } |
| if (scriptElement == null) { |
| IResource resource = adaptable.getAdapter(IResource.class); |
| if (resource != null |
| && resource.getType() != IResource.ROOT) { |
| while (scriptElement == null |
| && resource.getType() != IResource.PROJECT) { |
| resource = resource.getParent(); |
| scriptElement = resource |
| .getAdapter(IModelElement.class); |
| } |
| |
| if (scriptElement == null) { |
| scriptElement = DLTKCore.create(resource); |
| } |
| } |
| } |
| } |
| } |
| |
| // Check view |
| if (scriptElement == null) { |
| IWorkbenchPart part = DLTKUIPlugin.getActivePage().getActivePart(); |
| if (part instanceof ContentOutline) { |
| part = DLTKUIPlugin.getActivePage().getActiveEditor(); |
| } |
| |
| if (part instanceof IViewPartInputProvider) { |
| Object provider = ((IViewPartInputProvider) part) |
| .getViewPartInput(); |
| if (provider instanceof IModelElement) { |
| scriptElement = (IModelElement) provider; |
| } |
| } |
| } |
| |
| if (scriptElement == null || scriptElement |
| .getElementType() == IModelElement.SCRIPT_MODEL) { |
| try { |
| IScriptProject[] projects = DLTKCore.create(getWorkspaceRoot()) |
| .getScriptProjects(); |
| if (projects.length == 1) { |
| scriptElement = projects[0]; |
| } |
| } catch (ModelException e) { |
| DLTKUIPlugin.log(e); |
| } |
| } |
| |
| return scriptElement; |
| } |
| |
| /** |
| * Returns the recommended maximum width for text fields (in pixels). This |
| * method requires that createContent has been called before this method is |
| * call. Subclasses may override to change the maximum width for text |
| * fields. |
| * |
| * @return the recommended maximum width for text fields. |
| */ |
| protected int getMaxFieldWidth() { |
| return convertWidthInCharsToPixels(40); |
| } |
| |
| /** |
| * Creates the necessary controls (label, text field and browse button) to |
| * edit the source folder location. The method expects that the parent |
| * composite uses a <code>GridLayout</code> as its layout manager and that |
| * the grid layout has at least 3 columns. |
| * |
| * @param parent |
| * the parent composite |
| * @param nColumns |
| * the number of columns to span. This number must be |
| * greater or equal three |
| */ |
| protected void createContainerControls(Composite parent, int nColumns) { |
| containerDialogField.doFillIntoGrid(parent, nColumns); |
| LayoutUtil.setWidthHint(containerDialogField.getTextControl(null), |
| getMaxFieldWidth()); |
| } |
| |
| /** |
| * Sets the focus to the source folder's text field. |
| */ |
| protected void setFocusOnContainer() { |
| containerDialogField.setFocus(); |
| } |
| |
| /* |
| * Overridden in NewSourceModuleInPackagePage |
| */ |
| void containerChangeControlPressed(DialogField field) { |
| IScriptFolder root = chooseContainer(); |
| if (root != null) { |
| setScriptFolder(root, true); |
| } |
| } |
| |
| private void containerDialogFieldChanged(DialogField field) { |
| if (field == containerDialogField) { |
| containerStatus = containerChanged(); |
| } |
| // tell all others |
| handleFieldChanged(CONTAINER); |
| } |
| |
| // ----------- validation ---------- |
| protected abstract String getRequiredNature(); |
| |
| /** |
| * This method is a hook which gets called after the source folder's text |
| * input field has changed. This default implementation updates the model |
| * and returns an error status. The underlying model is only valid if the |
| * returned status is OK. |
| * |
| * @return the model's error status |
| */ |
| protected IStatus containerChanged() { |
| StatusInfo status = new StatusInfo(); |
| currRoot = null; |
| String str = getScriptFolderText(); |
| if (str.length() == 0) { |
| status.setError( |
| NewWizardMessages.NewContainerWizardPage_error_EnterContainerName); |
| return status; |
| } |
| IPath path = new Path(str); |
| IResource res = workspaceRoot.findMember(path); |
| if (res != null) { |
| int resType = res.getType(); |
| if (resType == IResource.PROJECT || resType == IResource.FOLDER) { |
| IProject proj = res.getProject(); |
| if (!proj.isOpen()) { |
| status.setError(Messages.format( |
| NewWizardMessages.NewContainerWizardPage_error_ProjectClosed, |
| proj.getFullPath().toString())); |
| return status; |
| } |
| |
| IScriptProject jproject = DLTKCore.create(proj); |
| if (resType == IResource.PROJECT) |
| currRoot = jproject.getProjectFragment(res) |
| .getScriptFolder(""); //$NON-NLS-1$ |
| else { |
| IProjectFragment[] fragments = null; |
| try { |
| fragments = jproject.getProjectFragments(); |
| } catch (ModelException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| } |
| if (fragments != null) { |
| IProjectFragment projectFragment = null; |
| for (IProjectFragment fragment : fragments) { |
| if (fragment.getPath().isPrefixOf(path)) { |
| projectFragment = fragment; |
| break; |
| } |
| } |
| if (projectFragment != null) { |
| IPath fragmentPath = projectFragment.getPath(); |
| currRoot = projectFragment |
| .getScriptFolder(path.removeFirstSegments( |
| fragmentPath.segmentCount())); |
| } |
| } |
| } |
| |
| if (res.exists()) { |
| try { |
| // if |
| // (!DLTKLanguageManager.hasScriptNature(jproject.getProject())) |
| // { |
| String nature = getRequiredNature(); |
| if (nature != null && !proj.hasNature(nature)) { |
| if (resType == IResource.PROJECT) { |
| status.setError( |
| NewWizardMessages.NewContainerWizardPage_warning_NotAScriptProject); |
| } else { |
| status.setWarning( |
| NewWizardMessages.NewContainerWizardPage_warning_NotInAScriptProject); |
| } |
| return status; |
| } |
| // } |
| |
| } catch (CoreException e) { |
| status.setWarning( |
| NewWizardMessages.NewContainerWizardPage_warning_NotAScriptProject); |
| } |
| } |
| return status; |
| } |
| status.setError(Messages.format( |
| NewWizardMessages.NewContainerWizardPage_error_NotAFolder, |
| str)); |
| return status; |
| } |
| status.setError(Messages.format( |
| NewWizardMessages.NewContainerWizardPage_error_ContainerDoesNotExist, |
| str)); |
| return status; |
| } |
| |
| // -------- update message ---------------- |
| /** |
| * Hook method that gets called when a field on this page has changed. For |
| * this page the method gets called when the source folder field changes. |
| * <p> |
| * Every sub type is responsible to call this method when a field on its |
| * page has changed. Subtypes override (extend) the method to add |
| * verification when a own field has a dependency to an other field. For |
| * example the class name input must be verified again when the package |
| * field changes (check for duplicated class names). |
| * |
| * @param fieldName |
| * The name of the field that has changed (field id). |
| * For the source folder the field id is |
| * <code>CONTAINER</code> |
| */ |
| protected void handleFieldChanged(String fieldName) { |
| } |
| |
| // ---- get ---------------- |
| /** |
| * Returns the workspace root. |
| * |
| * @return the workspace root |
| */ |
| protected IWorkspaceRoot getWorkspaceRoot() { |
| return workspaceRoot; |
| } |
| |
| /** |
| * Returns the <code>IProjectFragment</code> that corresponds to the current |
| * value of the source folder field. |
| * |
| * @return the IProjectFragment or <code>null</code> if the current source |
| * folder value is not a valid package fragment root |
| * |
| */ |
| public IProjectFragment getProjectFragment() { |
| if (currRoot == null) |
| return null; |
| IProjectFragment fragment = (IProjectFragment) currRoot |
| .getAncestor(IModelElement.PROJECT_FRAGMENT); |
| if (fragment != null) |
| return fragment; |
| IScriptProject project = currRoot.getScriptProject(); |
| try { |
| if (project.exists()) { |
| IProjectFragment[] roots = project.getProjectFragments(); |
| for (int i = 0; i < roots.length; i++) { |
| if (roots[i].getKind() == IProjectFragment.K_SOURCE) { |
| return roots[i]; |
| } |
| } |
| } |
| } catch (ModelException e) { |
| } |
| return null; |
| } |
| |
| public IScriptFolder getScriptFolder() { |
| return currRoot; |
| } |
| |
| /** |
| * Returns the current text of source folder text field. |
| * |
| * @return the text of the source folder text field |
| */ |
| public String getScriptFolderText() { |
| return containerDialogField.getText(); |
| } |
| |
| /** |
| * Sets the current source folder (model and text field) to the given |
| * package fragment root. |
| * |
| * @param root |
| * The new root. |
| * @param canBeModified |
| * if <code>false</code> the source folder field |
| * can not be changed by the user. If |
| * <code>true</code> the field is editable |
| */ |
| public void setScriptFolder(IScriptFolder root, boolean canBeModified) { |
| currRoot = root; |
| String str = (root == null) ? "" //$NON-NLS-1$ |
| : root.getPath().makeRelative().toString(); |
| containerDialogField.setText(str); |
| containerDialogField.setEnabled(canBeModified); |
| } |
| |
| // ------------- choose source container dialog |
| /** |
| * Opens a selection dialog that allows to select a source container. |
| * |
| * @return returns the selected package fragment root or <code>null</code> |
| * if the dialog has been canceled. The caller typically sets the |
| * result to the container input field. |
| * <p> |
| * Clients can override this method if they want to offer a |
| * different dialog. |
| * </p> |
| * |
| * |
| */ |
| protected IScriptFolder chooseContainer() { |
| IModelElement initElement = getProjectFragment(); |
| |
| ViewerFilter filter = new ContainerViewerFilter(); |
| |
| return doChooseContainer(initElement, filter, null); |
| } |
| |
| /** |
| * Called by {@link #chooseContainer()} with initial element and viewer |
| * filter. |
| * |
| * @param initElement |
| * initially selected element |
| * @param filter |
| * viewer filter |
| * @param validator |
| * selection validator, may be null |
| */ |
| protected IScriptFolder doChooseContainer(IModelElement initElement, |
| ViewerFilter filter, ISelectionStatusValidator validator) { |
| StandardModelElementContentProvider provider = new StandardModelElementContentProvider(); |
| ILabelProvider labelProvider = new ModelElementLabelProvider( |
| ModelElementLabelProvider.SHOW_DEFAULT); |
| ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog( |
| getShell(), labelProvider, provider); |
| |
| dialog.setComparator(new ModelElementSorter()); |
| dialog.setTitle( |
| NewWizardMessages.NewContainerWizardPage_ChooseSourceContainerDialog_title); |
| dialog.setMessage( |
| NewWizardMessages.NewContainerWizardPage_ChooseSourceContainerDialog_description); |
| dialog.addFilter(filter); |
| if (validator != null) { |
| dialog.setValidator(validator); |
| } |
| dialog.setInput(DLTKCore.create(workspaceRoot)); |
| dialog.setInitialSelection(initElement); |
| dialog.setHelpAvailable(false); |
| if (dialog.open() == Window.OK) { |
| Object element = dialog.getFirstResult(); |
| if (element instanceof IScriptProject) { |
| IScriptProject jproject = (IScriptProject) element; |
| return jproject.getProjectFragment(jproject.getResource()) |
| .getScriptFolder(""); //$NON-NLS-1$ |
| } else if (element instanceof IScriptFolder) { |
| return (IScriptFolder) element; |
| } else if (element instanceof IProjectFragment) { |
| return ((IProjectFragment) element).getScriptFolder(""); //$NON-NLS-1$ |
| } |
| return null; |
| } |
| |
| return null; |
| } |
| } |