blob: daa5558157bec6d05a71d1cb1916b67c6e461aef [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2012 QNX Software Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* QNX Software Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.wizards.classwizard;
import java.util.ArrayList;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.corext.util.CModelUtil;
import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.cdt.internal.ui.dialogs.StatusUtil;
import org.eclipse.cdt.internal.ui.dialogs.TypedViewerFilter;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.StringDialogField;
import org.eclipse.cdt.ui.CElementContentProvider;
import org.eclipse.cdt.ui.CElementLabelProvider;
import org.eclipse.cdt.ui.CElementSorter;
import org.eclipse.cdt.utils.PathUtil;
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.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.dialogs.SelectionStatusDialog;
public class SourceFileSelectionDialog extends SelectionStatusDialog {
TreeViewer fViewer;
private final ITreeContentProvider fContentProvider = new CElementContentProvider();
private final ILabelProvider fLabelProvider = new CElementLabelProvider(CElementLabelProvider.SHOW_DEFAULT);
IStatus fCurrStatus = new StatusInfo();
IStatus fFolderNameStatus = new StatusInfo();
IStatus fFileNameStatus = new StatusInfo();
ICElement fInput;
private int fWidth = 60;
private int fHeight = 18;
StringDialogField fFolderNameDialogField;
StringDialogField fFileNameDialogField;
private IWorkspaceRoot fWorkspaceRoot;
private final FieldsAdapter fFieldsAdapter = new FieldsAdapter();
private ICElement fCurrentFolder = null;
private String fCurrentFileString = null;
String fInitialFolderName = null;
String fInitialFileName = null;
private final class FieldsAdapter extends SelectionAdapter
implements ISelectionChangedListener, IDoubleClickListener, IDialogFieldListener {
// -- SelectionAdapter --
@Override
public void widgetDefaultSelected(SelectionEvent e) {
doStatusUpdate();
if (fCurrStatus.isOK())
buttonPressed(IDialogConstants.OK_ID);
}
// -- ISelectionChangedListener --
@Override
public void selectionChanged(SelectionChangedEvent event) {
setResult(((IStructuredSelection) event.getSelection()).toList());
ISelection sel = event.getSelection();
if (sel instanceof IStructuredSelection) {
Object obj = ((IStructuredSelection) sel).getFirstElement();
if (obj instanceof ICElement) {
ICElement elem = (ICElement) obj;
IPath path = elem.getPath();
String fileName = fFileNameDialogField.getText();
String folderName = fFolderNameDialogField.getText();
if (elem instanceof ICContainer || elem instanceof ICProject) {
folderName = path.toString();
} else {
folderName = path.removeLastSegments(1).toString();
fileName = path.lastSegment();
}
setPathFields(folderName, fileName);
}
}
doStatusUpdate();
}
// -- IDoubleClickListener --
@Override
public void doubleClick(DoubleClickEvent event) {
doStatusUpdate();
ISelection selection = event.getSelection();
if (selection instanceof IStructuredSelection) {
Object item = ((IStructuredSelection) selection).getFirstElement();
if (fCurrStatus.getSeverity() != IStatus.ERROR) {
if (item instanceof ITranslationUnit) {
setResult(((IStructuredSelection) selection).toList());
close();
return;
}
}
if (fViewer.getExpandedState(item))
fViewer.collapseToLevel(item, 1);
else
fViewer.expandToLevel(item, 1);
}
}
// -- IDialogFieldListener --
@Override
public void dialogFieldChanged(DialogField field) {
if (field == fFolderNameDialogField) {
fFolderNameStatus = folderNameChanged();
fFileNameStatus = fileNameChanged();
} else if (field == fFileNameDialogField) {
fFileNameStatus = fileNameChanged();
}
doStatusUpdate();
}
}
static final Class<?>[] FILTER_TYPES = new Class[] { ICModel.class, ICProject.class, ICContainer.class,
ITranslationUnit.class };
private final class Filter extends TypedViewerFilter {
private Filter() {
super(FILTER_TYPES);
}
@Override
public boolean select(Viewer viewer, Object parent, Object obj) {
if (obj instanceof ICElement) {
ICElement elem = (ICElement) obj;
if (!(fInput instanceof ICModel)) {
return elem.getCProject().equals(fInput.getCProject());
}
return true;
}
return super.select(viewer, parent, obj);
}
}
/**
* Constructs an instance of <code>ElementTreeSelectionDialog</code>.
*
* @param parent
* The parent shell for the dialog
*/
public SourceFileSelectionDialog(Shell parent) {
super(parent);
fWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
fInput = CoreModel.create(fWorkspaceRoot);
fFolderNameDialogField = new StringDialogField();
fFolderNameDialogField.setDialogFieldListener(fFieldsAdapter);
fFolderNameDialogField.setLabelText(NewClassWizardMessages.SourceFileSelectionDialog_folderName_label);
fFileNameDialogField = new StringDialogField();
fFileNameDialogField.setDialogFieldListener(fFieldsAdapter);
fFileNameDialogField.setLabelText(NewClassWizardMessages.SourceFileSelectionDialog_fileName_label);
setResult(new ArrayList<>(0));
setStatusLineAboveButtons(true);
int shellStyle = getShellStyle();
setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
}
/**
* Sets the tree input.
*
* @param input
* the tree input.
*/
public void setInput(ICElement input) {
fInput = input;
}
protected void doStatusUpdate() {
// status of all used components
IStatus[] status = new IStatus[] { fFolderNameStatus, fFileNameStatus };
// the mode severe status will be displayed and the ok button enabled/disabled.
updateStatus(status);
}
/**
* Updates the status line and the ok button according to the given status
*
* @param status status to apply
*/
@Override
protected void updateStatus(IStatus status) {
fCurrStatus = status;
super.updateStatus(status);
}
/**
* Updates the status line and the ok button according to the status evaluate from
* an array of status. The most severe error is taken. In case that two status with
* the same severity exists, the status with lower index is taken.
*
* @param status the array of status
*/
protected void updateStatus(IStatus[] status) {
updateStatus(StatusUtil.getMostSevere(status));
}
IStatus folderNameChanged() {
StatusInfo status = new StatusInfo();
fCurrentFolder = null;
String str = fFolderNameDialogField.getText();
if (str.length() == 0) {
status.setError(NewClassWizardMessages.SourceFileSelectionDialog_error_EnterFolderName);
return status;
}
IPath path = new Path(str);
IResource res = fWorkspaceRoot.findMember(path);
if (res != null && res.exists()) {
int resType = res.getType();
if (resType == IResource.PROJECT || resType == IResource.FOLDER) {
IProject proj = res.getProject();
if (!proj.isOpen()) {
status.setError(NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_error_NotAFolder, str));
return status;
}
ICElement e = CoreModel.getDefault().create(res.getFullPath());
fCurrentFolder = CModelUtil.getSourceFolder(e);
if (fCurrentFolder == null) {
status.setError(
NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_error_NotASourceFolder, str));
return status;
}
if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) {
if (resType == IResource.PROJECT) {
status.setError(NewClassWizardMessages.SourceFileSelectionDialog_warning_NotACProject);
return status;
}
status.setWarning(NewClassWizardMessages.SourceFileSelectionDialog_warning_NotInACProject);
}
} else {
status.setError(NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_error_NotAFolder, str));
return status;
}
} else {
status.setError(NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_error_FolderDoesNotExist, str));
return status;
}
return status;
}
IStatus fileNameChanged() {
StatusInfo status = new StatusInfo();
fCurrentFileString = null;
ICElement existingFile = null;
String str = fFileNameDialogField.getText();
if (str.length() == 0) {
status.setError(NewClassWizardMessages.SourceFileSelectionDialog_error_EnterFileName);
return status;
}
if (fCurrentFolder != null) {
IPath folderPath = fCurrentFolder.getPath();
IPath path = new Path(str);
IResource res = fWorkspaceRoot.findMember(folderPath.append(path));
if (res == null)
res = fWorkspaceRoot.findMember(path);
if (res != null && res.exists()) {
int resType = res.getType();
if (resType == IResource.FILE) {
IProject proj = res.getProject();
if (!proj.isOpen()) {
status.setError(NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_error_NotAFile, str));
return status;
}
ICElement e = CoreModel.getDefault().create(res.getFullPath());
if (e instanceof ITranslationUnit) {
existingFile = e;
}
if (existingFile == null) {
status.setError(
NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_error_NotASourceFile, str));
return status;
}
if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) {
status.setWarning(NewClassWizardMessages.SourceFileSelectionDialog_warning_NotInACProject);
}
} else {
status.setError(NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_error_NotAFile, str));
return status;
}
}
}
if (existingFile != null) {
status.setWarning(NLS.bind(NewClassWizardMessages.SourceFileSelectionDialog_warning_SourceFileExists, str));
}
fCurrentFileString = str;
return status;
}
@Override
public int open() {
super.open();
return getReturnCode();
}
/**
* Handles cancel button pressed event.
*/
@Override
protected void cancelPressed() {
setResult(null);
super.cancelPressed();
}
/*
* @see SelectionStatusDialog#computeResult()
*/
@Override
protected void computeResult() {
setResult(((IStructuredSelection) fViewer.getSelection()).toList());
}
@Override
public void create() {
BusyIndicator.showWhile(null, () -> {
superCreate();
fViewer.setSelection(new StructuredSelection(getInitialElementSelections()), true);
setPathFields(fInitialFolderName, fInitialFileName);
fFileNameDialogField.setFocus();
doStatusUpdate();
});
}
void superCreate() {
super.create();
}
/*
* @see Dialog#createDialogArea(Composite)
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
// Label messageLabel = createMessageArea(composite);
int nColumns = 4;
fFolderNameDialogField.doFillIntoGrid(composite, nColumns - 1);
DialogField.createEmptySpace(composite);
LayoutUtil.setWidthHint(fFolderNameDialogField.getTextControl(null), getMaxFieldWidth());
TreeViewer treeViewer = createTreeViewer(composite);
GridData data = new GridData(GridData.FILL_BOTH);
data.widthHint = convertWidthInCharsToPixels(fWidth);
data.heightHint = convertHeightInCharsToPixels(fHeight);
Tree treeWidget = treeViewer.getTree();
treeWidget.setLayoutData(data);
treeWidget.setFont(parent.getFont());
fFileNameDialogField.doFillIntoGrid(composite, nColumns - 1);
DialogField.createEmptySpace(composite);
LayoutUtil.setWidthHint(fFileNameDialogField.getTextControl(null), getMaxFieldWidth());
return composite;
}
/**
* 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(60);
}
/**
* Creates the tree viewer.
*
* @param parent
* the parent composite
* @return the tree viewer
*/
protected TreeViewer createTreeViewer(Composite parent) {
int style = (SWT.BORDER | SWT.SINGLE);
fViewer = new TreeViewer(new Tree(parent, style));
fViewer.setContentProvider(fContentProvider);
fViewer.setLabelProvider(fLabelProvider);
fViewer.addSelectionChangedListener(fFieldsAdapter);
fViewer.setSorter(new CElementSorter());
fViewer.addFilter(new Filter());
Tree tree = fViewer.getTree();
tree.addSelectionListener(fFieldsAdapter);
fViewer.addDoubleClickListener(fFieldsAdapter);
fViewer.setInput(fInput.getCModel());
return fViewer;
}
/**
* Returns the tree viewer.
*
* @return the tree viewer
*/
protected TreeViewer getTreeViewer() {
return fViewer;
}
/**
* @see org.eclipse.jface.window.Window#handleShellCloseEvent()
*/
@Override
protected void handleShellCloseEvent() {
super.handleShellCloseEvent();
//Handle the closing of the shell by selecting the close icon
if (getReturnCode() == CANCEL)
setResult(null);
}
public String getFolderName() {
if (fCurrentFolder != null) {
return fCurrentFolder.getPath().toString();
}
return null;
}
public String getFileName() {
return fCurrentFileString;
}
public IPath getFilePath() {
IPath path = null;
if (fCurrentFolder != null) {
path = fCurrentFolder.getPath();
if (fCurrentFileString != null)
path = path.append(fCurrentFileString);
} else if (fCurrentFileString != null) {
path = new Path(fCurrentFileString);
}
return path;
}
void setPathFields(String folderName, String fileName) {
fFolderNameDialogField.setTextWithoutUpdate(folderName != null ? folderName : ""); //$NON-NLS-1$
fFileNameDialogField.setTextWithoutUpdate(fileName != null ? fileName : ""); //$NON-NLS-1$
fFolderNameStatus = folderNameChanged();
fFileNameStatus = fileNameChanged();
}
/**
* Sets the initial selection. Convenience method.
*/
public void setInitialSelection(String folderName, String fileName) {
fInitialFileName = (fileName != null && fileName.length() > 0) ? fileName : null;
fInitialFolderName = null;
if (folderName != null && folderName.length() > 0) {
// find a folder that actually exists
IPath initialFolderPath = new Path(folderName);
final IPath folderPath = PathUtil.getValidEnclosingFolder(initialFolderPath);
if (folderPath != null) {
fInitialFolderName = folderPath.toString();
if (fInput != null) {
final ICElement[] foundElem = { /*base_folder*/ null, /*exact_folder*/ null, /*exact_file*/ null };
try {
fInput.accept(elem -> {
IPath path = elem.getPath();
if (path.isPrefixOf(folderPath)) {
if (foundElem[0] == null
|| path.segmentCount() > foundElem[0].getPath().segmentCount()) {
foundElem[0] = elem; /*base_folder*/
}
if (path.equals(folderPath)) {
foundElem[1] = elem; /*exact_folder*/
if (fInitialFileName == null)
return false; // no need to search children
} else if (fInitialFileName != null && elem.getElementName().equals(fInitialFileName)) {
foundElem[2] = elem; /*exact_file*/
return false; // no need to search children
}
return true;
}
return false;
});
ICElement selectedElement = foundElem[2]; /*exact_file*/
if (selectedElement == null)
selectedElement = foundElem[1]; /*exact_folder*/
if (selectedElement == null)
selectedElement = foundElem[0]; /*base_folder*/
if (selectedElement != null) {
setInitialSelections(new Object[] { selectedElement });
}
} catch (CoreException e) {
}
}
}
}
}
}