/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Created on Jan 17, 2006 */ package org.python.pydev.ui.wizards.files; import java.util.ArrayList; import java.util.TreeMap; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; 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.Path; import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData; import org.eclipse.jface.text.templates.persistence.TemplateStore; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; 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.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.List; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.model.WorkbenchLabelProvider; import org.python.pydev.core.IPythonPathNature; import org.python.pydev.core.log.Log; import org.python.pydev.editor.templates.PyContextType; import org.python.pydev.editor.templates.TemplateHelper; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.plugin.nature.PythonNature; import org.python.pydev.ui.UIConstants; import org.python.pydev.ui.dialogs.PythonPackageSelectionDialog; import org.python.pydev.ui.dialogs.SourceFolder; /** * The default creation page may be found at org.eclipse.ui.dialogs.WizardNewFileCreationPage */ public abstract class AbstractPythonWizardPage extends WizardPage implements KeyListener { private IStructuredSelection selection; private Text textSourceFolder; private Button btBrowseSourceFolder; protected Text textPackage; private Button btBrowsePackage; protected Text textName; private String initialTextName = ""; /** * It is not null only when the source folder was correctly validated */ private IContainer validatedSourceFolder; /** * It is not null only when the package was correctly validated */ private IContainer validatedPackage; private String packageText; /** * This is the project */ private IProject validatedProject; /** * It is not null only when the name was correctly validated */ private String validatedName; private Text textProject; private Button btBrowseProject; public IContainer getValidatedSourceFolder() { return validatedSourceFolder; } public IContainer getValidatedPackage() { return validatedPackage; } public String getPackageText() { return packageText; } public String getValidatedName() { return validatedName; } public IProject getValidatedProject() { return validatedProject; } protected AbstractPythonWizardPage(String pageName, IStructuredSelection selection) { super(pageName); setPageComplete(false); this.selection = selection; } private Text lastWithFocus; protected String lastWithFocusStr; private List templateList; private TreeMap<String, TemplatePersistenceData> nameToTemplateData; private Label labelWarningWillCreate; private Label labelWarningImageWillCreate; private void setFocusOn(Text txt, String string) { if (txt != null) { //System.out.println("seting focus on:"+string); txt.setFocus(); lastWithFocus = txt; lastWithFocusStr = string; } } public void resetFocusOnLast() { if (lastWithFocus != null) { //System.out.println("reseting focus on:"+lastWithFocusStr); lastWithFocus.setFocus(); } } @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible == true) { resetFocusOnLast(); } } public void createControl(Composite parent) { // top level group Composite topLevel = new Composite(parent, SWT.NONE); GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 3; topLevel.setLayout(gridLayout); topLevel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL)); topLevel.setFont(parent.getFont()); boolean previousFilled = true; //create either source folder if (shouldCreateSourceFolderSelect()) { previousFilled = createSourceFolderSelect(topLevel); } else { //or the project selection previousFilled = createProjectSelect(topLevel); } //always call the package create (but note that it may either create the package or not). boolean createPackageSelectFilled = createPackageSelect(topLevel, previousFilled); if (shouldCreatePackageSelect()) { previousFilled = createPackageSelectFilled; } //always create the name createNameSelect(topLevel, previousFilled); // Show description on opening setErrorMessage(null); setMessage(null); setControl(topLevel); } /** * Decide whether a source folder must be selected to complete the dialog. * * Subclasses can override. * * @return true if a source folder should be selected and false if it shouldn't */ protected boolean shouldCreateSourceFolderSelect() { return true; } /** * Subclasses should override to decide whether a package must be selected to complete the dialog. * @return true if a package should be selected and false if it shouldn't */ protected abstract boolean shouldCreatePackageSelect(); /** * By default only creates the templates box if a package must be selected. */ protected boolean shouldCreateTemplates() { return shouldCreatePackageSelect(); } /** * @param topLevel */ protected void createNameSelect(Composite topLevel, boolean setFocus) { createNameLabel(topLevel); textName = new Text(topLevel, SWT.BORDER); textName.addKeyListener(this); setLayout(null, textName, null); if (initialTextName != null) { textName.setText(initialTextName); } if (setFocus) { setFocusOn(textName, "name"); textName.setSelection(textName.getText().length()); } //just create an empty to complete the line (that needs 3 items in the layout) Label label = new Label(topLevel, SWT.NONE); label.setText(""); if (shouldCreateTemplates()) { final TemplateStore templateStore = TemplateHelper.getTemplateStore(); if (templateStore != null) { TemplatePersistenceData[] templateData = templateStore.getTemplateData(false); if (templateData != null && templateData.length > 0) { //create the template selection label = new Label(topLevel, SWT.NONE); label.setText("Template"); templateList = new List(topLevel, SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER); fillTemplateOptions(templateData, templateList); //if in the text, pressing down should go to the templates list textName.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { if (e.keyCode == SWT.ARROW_DOWN) { templateList.setFocus(); e.doit = false; } } }); textName.addTraverseListener(new TraverseListener() { public void keyTraversed(TraverseEvent e) { if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { templateList.setFocus(); e.doit = false; } } }); Link link = new Link(topLevel, SWT.NONE); link.setText("<a>Config...</a>"); link.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, "org.python.pydev.prefs.template", null, null); dialog.open(); //Fill it after having the settings edited. TemplatePersistenceData[] templateData = templateStore.getTemplateData(false); if (templateData != null && templateData.length > 0) { fillTemplateOptions(templateData, templateList); } else { fillTemplateOptions(new TemplatePersistenceData[0], templateList); } } public void widgetDefaultSelected(SelectionEvent e) { } }); setLayout(label, templateList, link); } } } } protected Label createNameLabel(Composite topLevel) { Label label = new Label(topLevel, SWT.NONE); label.setText("Name"); GridData data = new GridData(); data.grabExcessHorizontalSpace = false; label.setLayoutData(data); return label; } /** * @return the data for the selected template or null if not available. */ public TemplatePersistenceData getSelectedTemplate() { if (templateList != null && nameToTemplateData != null) { String[] sel = templateList.getSelection(); if (sel != null && sel.length > 0) { return nameToTemplateData.get(sel[0]); } } return null; } /** * Sets the template options in the passed list (swt) */ private void fillTemplateOptions(TemplatePersistenceData[] templateData, List list) { nameToTemplateData = new TreeMap<String, TemplatePersistenceData>(); for (TemplatePersistenceData data : templateData) { if (PyContextType.PY_MODULES_CONTEXT_TYPE.equals(data.getTemplate().getContextTypeId())) { String name = data.getTemplate().getName(); nameToTemplateData.put(name, data); } } ArrayList<String> lst = new ArrayList<String>(nameToTemplateData.keySet()); list.setItems(lst.toArray(new String[lst.size()])); list.setSelection(0); } private boolean createProjectSelect(Composite topLevel) { Label label; label = new Label(topLevel, SWT.NONE); label.setText("Project"); textProject = new Text(topLevel, SWT.BORDER); textProject.addKeyListener(this); btBrowseProject = new Button(topLevel, SWT.NONE); setLayout(label, textProject, btBrowseProject); setFocusOn(textProject, "project"); btBrowseProject.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), new WorkbenchLabelProvider()); dialog.setTitle("Project selection"); dialog.setTitle("Select a project."); dialog.setElements(ResourcesPlugin.getWorkspace().getRoot().getProjects()); dialog.open(); Object[] result = dialog.getResult(); if (result != null && result.length > 0) { textProject.setText(((IProject) result[0]).getName()); } } public void widgetDefaultSelected(SelectionEvent e) { } }); Object element = selection.getFirstElement(); if (element instanceof IAdaptable) { IAdaptable adaptable = (IAdaptable) element; element = adaptable.getAdapter(IResource.class); } if (element instanceof IResource) { IResource f = (IResource) element; element = f.getProject(); } if (element instanceof IProject) { textProject.setText(((IProject) element).getName()); return true; } return false; } /** * @param topLevel * @return */ private boolean createPackageSelect(Composite topLevel, boolean setFocus) { if (shouldCreatePackageSelect()) { Label label; label = new Label(topLevel, SWT.NONE); label.setText("Package"); textPackage = new Text(topLevel, SWT.BORDER); textPackage.addKeyListener(this); btBrowsePackage = new Button(topLevel, SWT.NONE); setLayout(label, textPackage, btBrowsePackage); labelWarningImageWillCreate = new Label(topLevel, SWT.NONE); labelWarningImageWillCreate.setVisible(false); labelWarningImageWillCreate.setImage(PydevPlugin.getImageCache().get(UIConstants.WARNING)); labelWarningWillCreate = new Label(topLevel, SWT.NONE); labelWarningWillCreate.setText("Note: package not found (will be created)."); labelWarningWillCreate.setVisible(false); setLayout(labelWarningImageWillCreate, labelWarningWillCreate, null); //just create an empty to complete the line (that needs 3 items in the layout) new Label(topLevel, SWT.NONE); if (setFocus) { setFocusOn(textPackage, "package"); } btBrowsePackage.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { try { PythonPackageSelectionDialog dialog = new PythonPackageSelectionDialog(getShell(), false); dialog.open(); Object firstResult = dialog.getFirstResult(); if (firstResult instanceof SourceFolder) { //it is the default package SourceFolder f = (SourceFolder) firstResult; textPackage.setText(""); textSourceFolder.setText(f.folder.getFullPath().toString()); } if (firstResult instanceof org.python.pydev.ui.dialogs.Package) { org.python.pydev.ui.dialogs.Package f = (org.python.pydev.ui.dialogs.Package) firstResult; textPackage.setText(f.getPackageName()); textSourceFolder.setText(f.sourceFolder.folder.getFullPath().toString()); } } catch (Exception e1) { Log.log(e1); } } public void widgetDefaultSelected(SelectionEvent e) { } }); } Object element = selection.getFirstElement(); try { if (element instanceof IAdaptable) { IAdaptable adaptable = (IAdaptable) element; element = adaptable.getAdapter(IFile.class); if (element == null) { element = adaptable.getAdapter(IFolder.class); } } if (element instanceof IFile) { IFile f = (IFile) element; element = f.getParent(); } if (element instanceof IFolder) { IFolder f = (IFolder) element; String srcPath = getSrcFolderFromFolder(f); if (srcPath == null) { return false; } String complete = f.getFullPath().toString(); if (complete.startsWith(srcPath)) { complete = complete.substring(srcPath.length()).replace('/', '.'); if (complete.startsWith(".")) { complete = complete.substring(1); } if (shouldCreatePackageSelect()) { textPackage.setText(complete); } else { initialTextName = complete; } } } } catch (Exception e) { Log.log(e); } if (shouldCreatePackageSelect()) { return textPackage.getText().length() > 0; } else { return false; } } /** * @param topLevel * @return */ private boolean createSourceFolderSelect(Composite topLevel) { Label label; label = new Label(topLevel, SWT.NONE); label.setText("Source Folder"); textSourceFolder = new Text(topLevel, SWT.BORDER); textSourceFolder.addKeyListener(this); btBrowseSourceFolder = new Button(topLevel, SWT.NONE); setLayout(label, textSourceFolder, btBrowseSourceFolder); btBrowseSourceFolder.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { try { PythonPackageSelectionDialog dialog = new PythonPackageSelectionDialog(getShell(), true); dialog.open(); Object firstResult = dialog.getFirstResult(); if (firstResult instanceof SourceFolder) { SourceFolder f = (SourceFolder) firstResult; textSourceFolder.setText(f.folder.getFullPath().toString()); } } catch (Exception e1) { Log.log(e1); } } public void widgetDefaultSelected(SelectionEvent e) { } }); Object element = selection.getFirstElement(); try { if (element instanceof IAdaptable) { IAdaptable adaptable = (IAdaptable) element; element = adaptable.getAdapter(IFile.class); if (element == null) { element = adaptable.getAdapter(IProject.class); } if (element == null) { element = adaptable.getAdapter(IFolder.class); } } if (element instanceof IFile) { IFile f = (IFile) element; element = f.getParent(); } if (element instanceof IProject) { IPythonPathNature nature = PythonNature.getPythonPathNature((IProject) element); if (nature != null) { String[] srcPaths = PythonNature.getStrAsStrItems(nature.getProjectSourcePath(true)); if (srcPaths.length > 0) { textSourceFolder.setText(srcPaths[0]); return true; } } } if (element instanceof IFolder) { IFolder f = (IFolder) element; String srcPath = getSrcFolderFromFolder(f); if (srcPath == null) { return true; } textSourceFolder.setText(srcPath); return true; } } catch (Exception e) { Log.log(e); } return false; } /** * @param f * @return * @throws CoreException */ public String getSrcFolderFromFolder(IFolder f) throws CoreException { IPythonPathNature nature = PythonNature.getPythonPathNature(f.getProject()); if (nature != null) { String[] srcPaths = PythonNature.getStrAsStrItems(nature.getProjectSourcePath(true)); String relFolder = f.getFullPath().toString() + "/"; for (String src : srcPaths) { if (relFolder.startsWith(src + "/")) { return src; } } } return null; } private void setLayout(Label label, Control text, Control bt) { GridData data; if (label != null) { data = new GridData(); data.grabExcessHorizontalSpace = false; label.setLayoutData(data); } if (text != null) { data = new GridData(GridData.FILL_HORIZONTAL); data.grabExcessHorizontalSpace = true; text.setLayoutData(data); } if (bt != null) { data = new GridData(); bt.setLayoutData(data); if (bt instanceof Button) { ((Button) bt).setText("Browse..."); } } } //listener interface public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { validatePage(); } protected void validatePage() { try { if (textProject != null) { if (checkError(checkValidProject(textProject.getText()))) { return; } } if (textSourceFolder != null) { if (checkError(checkValidSourceFolder(textSourceFolder.getText()))) { return; } } if (textPackage != null) { if (checkError(checkValidPackage(textPackage.getText()))) { return; } } if (textName != null) { if (checkError(checkValidName(textName.getText()))) { return; } } if (checkAdditionalErrors()) { return; } setErrorMessage(null); setMessage(getDescription()); setPageComplete(true); } catch (Exception e) { Log.log(e); setErrorMessage("Error while validating page:" + e.getMessage()); setPageComplete(false); } } /** * Subclasses may override to do additional error-checking. * @return */ protected boolean checkAdditionalErrors() { return false; } private String checkValidProject(String text) { validatedProject = null; if (text == null || text.trim().length() == 0) { return "The project name must be filled."; } IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IProject project = root.getProject(text); if (!project.exists()) { return "The project selected does not exist in the workspace."; } validatedProject = project; return null; } protected boolean checkError(String error) { if (error != null) { setErrorMessage(error); setPageComplete(false); return true; } return false; } private String checkValidName(String text) { validatedName = null; String error = checkNameText(text); if (error != null) { return error; } validatedName = text; return null; } protected String checkNameText(String text) { if (text == null || text.trim().length() == 0) { return "The name must be filled."; } if (shouldCreateSourceFolderSelect()) { if (validatedSourceFolder == null) { return "The source folder was not correctly validated."; } } else if (validatedProject == null) { return "The project was not correctly validated."; } if (shouldCreatePackageSelect()) { if (validatedPackage == null && packageText == null) { return "The package was not correctly validated."; } } if (text.indexOf(' ') != -1) { return "The name may not contain spaces"; } if (shouldCreatePackageSelect()) { //it is a new file not a new package if (text.indexOf('.') != -1) { return "The name may not contain dots"; } } //there are certainly other invalid chars, but let's leave it like that... char[] invalid = new char[] { '/', '\\', ',', '*', '(', ')', '{', '}', '[', ']' }; for (char c : invalid) { if (text.indexOf(c) != -1) { return "The name must not contain '" + c + "'."; } } if (text.endsWith(".")) { return "The name may not end with a dot"; } return null; } protected String checkValidPackage(String text) { validatedPackage = null; packageText = null; //there is a chance that the package is the default project, so, the validation below may not be valid. //if(text == null || text.trim().length() == 0 ){ //} String initialText = text; if (text.indexOf('/') != -1) { labelWarningImageWillCreate.setVisible(false); labelWarningWillCreate.setVisible(false); labelWarningWillCreate.getParent().layout(); return "The package name must not contain '/'."; } if (text.indexOf('\\') != -1) { labelWarningImageWillCreate.setVisible(false); labelWarningWillCreate.setVisible(false); labelWarningWillCreate.getParent().layout(); return "The package name must not contain '\\'."; } if (text.endsWith(".")) { labelWarningImageWillCreate.setVisible(false); labelWarningWillCreate.setVisible(false); labelWarningWillCreate.getParent().layout(); return "The package may not end with a dot"; } text = text.replace('.', '/'); if (validatedSourceFolder == null) { labelWarningImageWillCreate.setVisible(false); labelWarningWillCreate.setVisible(false); labelWarningWillCreate.getParent().layout(); return "The source folder was not correctly validated."; } IPath path = validatedSourceFolder.getFullPath().append(text); IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IResource resource = root.findMember(path); if (resource == null) { packageText = initialText; labelWarningImageWillCreate.setVisible(true); labelWarningWillCreate.setVisible(true); labelWarningWillCreate.getParent().layout(); return null; } labelWarningImageWillCreate.setVisible(false); labelWarningWillCreate.setVisible(false); labelWarningWillCreate.getParent().layout(); if (!(resource instanceof IContainer)) { return "The resource found for the package is not a valid container."; } if (!resource.exists()) { return "The package selected does not exist in the filesystem."; } validatedPackage = (IContainer) resource; return null; } private String checkValidSourceFolder(String text) throws CoreException { validatedSourceFolder = null; if (text == null || text.trim().length() == 0) { return "The source folder must be filled."; } IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IResource resource = root.findMember(new Path(text)); if (resource == null) { return "The source folder was not found in the workspace."; } if (!(resource instanceof IContainer)) { return "The source folder was found in the workspace but is not a container."; } IProject project = resource.getProject(); if (project == null) { return "Unable to find the project related to the source folder."; } IPythonPathNature nature = PythonNature.getPythonPathNature(project); if (nature == null) { return "The pydev nature is not configured on the project: " + project.getName(); } String full = resource.getFullPath().toString(); String[] srcPaths = PythonNature.getStrAsStrItems(nature.getProjectSourcePath(true)); for (String str : srcPaths) { if (str.equals(full)) { validatedSourceFolder = (IContainer) resource; return null; } } return "The selected source folder is not recognized as a valid source folder."; } }