/**
* Copyright (c) 2005-2013 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.
*/
package org.python.pydev.ui.wizards.files;
import java.util.List;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_core.structure.LinkedListWarningOnSlowOperations;
import org.python.pydev.shared_ui.UIConstants;
import org.python.pydev.ui.editors.TreeWithAddRemove;
public class PythonExistingSourceListGroup extends PythonExistingSourceGroup {
/**
* Tree with source folders
*/
private TreeWithAddRemove treeLinkTargets;
/**
* The source paths that are selected by this group, and are to be added to a project's
* list of referenced source locations.
*/
private List<IPath> linkTargets = new LinkedListWarningOnSlowOperations<IPath>();
/**
* Creates a new instance of the widget.
*
* @param parent The parent widget of the group.
* @param sourceChangeListener The listener that reacts to when a selection is made, or when a
* selection is removed.
*/
public PythonExistingSourceListGroup(Composite parent, SelectionListener sourceChangeListener) {
super();
createContents(parent, sourceChangeListener);
}
protected void createContents(Composite parent, final SelectionListener sourceChangeListener) {
GridData gd;
GridData data;
Label l2 = new Label(parent, SWT.NONE);
l2.setText(
"Project External Source Folders\n\nChoose external folders containing source that should be used for this project."
+ "\nThese folders will be automatically added to the PYTHONPATH\n(unless the 'Don't configure PYTHONPATH' option was selected).");
gd = new GridData();
gd.grabExcessHorizontalSpace = true;
gd.grabExcessVerticalSpace = false;
l2.setLayoutData(gd);
treeLinkTargets = new TreeWithAddRemove(parent, 0, null) {
private String sourceFolders = "";
@Override
protected String getButtonLabel(int i) {
switch (i) {
case 0:
return "Add external source folder";
default:
throw new AssertionError("Unexpected: " + i);
}
}
@Override
protected void customizeAddSomethingButton(Button addButton, final int nButton) {
super.customizeAddSomethingButton(addButton, nButton);
if (sourceChangeListener != null) {
addButton.addSelectionListener(sourceChangeListener);
}
}
@Override
protected void customizeRemSourceFolderButton(Button buttonRem) {
super.customizeRemSourceFolderButton(buttonRem);
if (sourceChangeListener != null) {
buttonRem.addSelectionListener(sourceChangeListener);
}
}
@Override
protected void handleAddButtonSelected(int nButton) {
if (nButton == 0) {
addItemWithDialog(new DirectoryDialog(getShell()));
IPath selected = getSelectedFolder();
if (selected != null) {
selectLinkTarget(selected);
}
} else {
throw new AssertionError("Unexpected");
}
}
@Override
protected void handleRemove() {
super.handleRemove();
if (folderWasSelected()) {
conflictCheck();
}
}
private IPath getSelectedFolder() {
if (!folderWasSelected()) {
return null;
}
String linkTarget = sourceFolders.substring(sourceFolders.lastIndexOf('|') + 1);
return Path.fromOSString(linkTarget);
}
private boolean folderWasSelected() {
String newSourceFolders = StringUtils.leftAndRightTrim(treeLinkTargets.getTreeItemsAsStr(), '|');
if (sourceFolders.equals(newSourceFolders)) {
// cancelled
return false;
}
sourceFolders = newSourceFolders;
return true;
}
@Override
protected String getImageConstant() {
return UIConstants.SOURCE_FOLDER_ICON;
}
@Override
protected int getNumberOfAddButtons() {
return 1;
}
};
data = new GridData(GridData.FILL_BOTH);
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
treeLinkTargets.setLayoutData(data);
}
/**
* Add the selected folder path to the list of all chosen paths. While doing so, check for conflicts.
* Issue a warning for the following selections:
* -folders that are subdirectories of other chosen folders, or contain other chosen folders
* -folders that have no .py files in them, or in one of their subdirectories
*
* Issue an error if the selection contains the destination of the link to be created. Don't add
* the selection to the list of source paths in case of an error.
*/
@Override
protected void selectLinkTarget(IPath linkPath) {
if (validateLinkPath(linkPath)) {
linkTargets.add(linkPath);
}
}
@Override
protected boolean validateLinkPath(IPath linkPath) {
if (!super.validateLinkPath(linkPath)) {
return false;
}
for (IPath otherPath : linkTargets) {
if (linkPath.isPrefixOf(otherPath) || otherPath.isPrefixOf(linkPath)) {
warningMessage = "Location '" + linkPath.lastSegment()
+ "' overlaps with the selected resource '"
+ otherPath.lastSegment()
+ "'. This can cause unexpected side-effects.";
break;
}
}
return true;
}
@Override
protected void conflictCheck() {
linkTargets.clear();
clearAllProblems();
String sourceFolders = StringUtils.leftAndRightTrim(treeLinkTargets.getTreeItemsAsStr(), '|');
if (sourceFolders.equals("")) {
return;
}
for (String pathString : StringUtils.splitAndRemoveEmptyTrimmed(sourceFolders, '|')) {
selectLinkTarget(Path.fromOSString(pathString));
}
}
/**
* Return all existing source paths chosen.
* @return
*/
public List<IPath> getLinkTargets() {
return linkTargets;
}
/**
* Return the most-recently selected source.
* @return
*/
@Override
public IPath getLinkTarget() {
if (linkTargets.size() == 0) {
return null;
}
return linkTargets.get(linkTargets.size() - 1);
}
}