blob: 11aa7871ecc6eb3e268d6fb404b89a4f43c5dd3f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.wizards.buildpaths.newsourcepage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.resources.IFolder;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.buildpath.BuildpathDelta;
import org.eclipse.jdt.internal.corext.buildpath.ClasspathModifier;
import org.eclipse.jdt.internal.corext.buildpath.IBuildpathModifierListener;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.preferences.ScrolledPageContent;
import org.eclipse.jdt.internal.ui.util.ViewerPane;
import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathBasePage;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathsBlock;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.CPListElement;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.CPListElementAttribute;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.StringDialogField;
public class NewSourceContainerWorkbookPage extends BuildPathBasePage implements IBuildpathModifierListener {
public static final String OPEN_SETTING= "org.eclipse.jdt.internal.ui.wizards.buildpaths.NewSourceContainerPage.openSetting"; //$NON-NLS-1$
private ListDialogField<CPListElement> fClassPathList;
private HintTextGroup fHintTextGroup;
private DialogPackageExplorer fPackageExplorer;
private SelectionButtonDialogField fUseFolderOutputs;
private final StringDialogField fOutputLocationField;
private DialogPackageExplorerActionGroup fActionGroup;
private IJavaProject fJavaProject;
private final IRunnableContext fContext;
private final BuildPathsBlock fBuildPathsBlock;
/**
* Constructor of the <code>NewSourceContainerWorkbookPage</code> which consists of
* a tree representing the project, a toolbar with the available actions, an area
* containing hyperlinks that perform the same actions as those in the toolbar but
* additionally with some short description.
*
* @param classPathList
* @param outputLocationField
* @param context a runnable context, can be <code>null</code>
* @param buildPathsBlock
*/
public NewSourceContainerWorkbookPage(ListDialogField<CPListElement> classPathList, StringDialogField outputLocationField, IRunnableContext context, BuildPathsBlock buildPathsBlock) {
fClassPathList= classPathList;
fOutputLocationField= outputLocationField;
fContext= context;
fBuildPathsBlock= buildPathsBlock;
fUseFolderOutputs= new SelectionButtonDialogField(SWT.CHECK);
fUseFolderOutputs.setSelection(false);
fUseFolderOutputs.setLabelText(NewWizardMessages.SourceContainerWorkbookPage_folders_check);
fPackageExplorer= new DialogPackageExplorer();
fHintTextGroup= new HintTextGroup();
}
/**
* Initialize the controls displaying
* the content of the java project and saving
* the '.classpath' and '.project' file.
*
* Must be called before initializing the
* controls using <code>getControl(Composite)</code>.
*
* @param javaProject the current java project
*/
@Override
public void init(IJavaProject javaProject) {
fJavaProject= javaProject;
fPackageExplorer.addPostSelectionChangedListener(fHintTextGroup);
fActionGroup.getResetAllAction().setBreakPoint(javaProject);
if (Display.getCurrent() != null) {
doUpdateUI();
} else {
Display.getDefault().asyncExec(this::doUpdateUI);
}
}
private void doUpdateUI() {
fPackageExplorer.setInput(fJavaProject);
boolean useFolderOutputs= false;
List<CPListElement> cpelements= fClassPathList.getElements();
for (int i= 0; i < cpelements.size() && !useFolderOutputs; i++) {
CPListElement cpe= cpelements.get(i);
if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
if (cpe.getAttribute(CPListElement.OUTPUT) != null) {
useFolderOutputs= true;
}
}
}
fUseFolderOutputs.setSelection(useFolderOutputs);
}
public void dispose() {
if (fActionGroup != null) {
fActionGroup.removeBuildpathModifierListener(this);
fActionGroup= null;
}
fPackageExplorer.removePostSelectionChangedListener(fHintTextGroup);
fPackageExplorer.dispose();
}
/**
* Initializes controls and return composite containing
* these controls.
*
* Before calling this method, make sure to have
* initialized this instance with a java project
* using <code>init(IJavaProject)</code>.
*
* @param parent the parent composite
* @return composite containing controls
*
* @see #init(IJavaProject)
*/
@Override
public Control getControl(Composite parent) {
final int[] sashWeight= {60};
final IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
preferenceStore.setDefault(OPEN_SETTING, true);
// ScrolledPageContent is needed for resizing on expand the expandable composite
ScrolledPageContent scrolledContent = new ScrolledPageContent(parent);
Composite body= scrolledContent.getBody();
body.setLayout(new GridLayout());
final SashForm sashForm= new SashForm(body, SWT.VERTICAL | SWT.NONE);
sashForm.setFont(sashForm.getFont());
ViewerPane pane= new ViewerPane(sashForm, SWT.BORDER | SWT.FLAT);
pane.setContent(fPackageExplorer.createControl(pane));
fPackageExplorer.setContentProvider();
final ExpandableComposite excomposite= new ExpandableComposite(sashForm, SWT.NONE, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);
excomposite.setFont(sashForm.getFont());
excomposite.setText(NewWizardMessages.NewSourceContainerWorkbookPage_HintTextGroup_title);
final boolean isExpanded= preferenceStore.getBoolean(OPEN_SETTING);
excomposite.setExpanded(isExpanded);
excomposite.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(excomposite);
if (parentScrolledComposite != null) {
boolean expanded= excomposite.isExpanded();
parentScrolledComposite.reflow(true);
adjustSashForm(sashWeight, sashForm, expanded);
preferenceStore.setValue(OPEN_SETTING, expanded);
}
}
});
excomposite.setClient(fHintTextGroup.createControl(excomposite));
fUseFolderOutputs.doFillIntoGrid(body, 1);
fActionGroup= new DialogPackageExplorerActionGroup(fHintTextGroup, fContext, fPackageExplorer, this);
fActionGroup.addBuildpathModifierListener(this);
fUseFolderOutputs.setDialogFieldListener(field -> {
boolean isUseFolders= fUseFolderOutputs.isSelected();
if (!isUseFolders) {
ResetAllOutputFoldersAction action= new ResetAllOutputFoldersAction(fContext, fJavaProject, fPackageExplorer) {
@Override
public void run() {
commitDefaultOutputFolder();
super.run();
}
};
action.addBuildpathModifierListener(NewSourceContainerWorkbookPage.this);
action.run();
}
fPackageExplorer.showOutputFolders(isUseFolders);
});
Composite outputLocation= new Composite(body, SWT.NONE);
outputLocation.setLayout(new GridLayout(2, false));
outputLocation.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
LayoutUtil.doDefaultLayout(outputLocation, new DialogField[] {fOutputLocationField }, true, SWT.DEFAULT, SWT.DEFAULT);
LayoutUtil.setHorizontalGrabbing(fOutputLocationField.getTextControl(null));
// Create toolbar with actions on the left
ToolBarManager tbm= fActionGroup.createLeftToolBarManager(pane);
pane.setTopCenter(null);
pane.setTopLeft(tbm.getControl());
// Create toolbar with help on the right
tbm= fActionGroup.createLeftToolBar(pane);
pane.setTopRight(tbm.getControl());
fHintTextGroup.setActionGroup(fActionGroup);
fPackageExplorer.setActionGroup(fActionGroup);
sashForm.setWeights(new int[] {60, 40});
adjustSashForm(sashWeight, sashForm, excomposite.isExpanded());
GridData gd= new GridData(GridData.FILL_BOTH);
PixelConverter converter= new PixelConverter(parent);
gd.heightHint= converter.convertHeightInCharsToPixels(20);
sashForm.setLayoutData(gd);
parent.layout(true);
return scrolledContent;
}
/**
* Adjust the size of the sash form.
*
* @param sashWeight the weight to be read or written
* @param sashForm the sash form to apply the new weights to
* @param isExpanded <code>true</code> if the expandable composite is
* expanded, <code>false</code> otherwise
*/
private void adjustSashForm(int[] sashWeight, SashForm sashForm, boolean isExpanded) {
if (isExpanded) {
int upperWeight= sashWeight[0];
sashForm.setWeights(new int[]{upperWeight, 100 - upperWeight});
}
else {
// TODO Dividing by 10 because of https://bugs.eclipse.org/bugs/show_bug.cgi?id=81939
sashWeight[0]= sashForm.getWeights()[0] / 10;
sashForm.setWeights(new int[]{95, 5});
}
sashForm.layout(true);
}
/**
* Get the scrolled page content of the given control by
* traversing the parents.
*
* @param control the control to get the scrolled page content for
* @return the scrolled page content or <code>null</code> if none found
*/
private ScrolledPageContent getParentScrolledComposite(Control control) {
Control parent= control.getParent();
while (!(parent instanceof ScrolledPageContent) && parent != null) {
parent= parent.getParent();
}
return (ScrolledPageContent) parent;
}
@Override
public List<CPListElement> getSelection() {
List<CPListElement> selectedList= new ArrayList<>();
IJavaProject project= fJavaProject;
try {
List<?> list= ((StructuredSelection)fPackageExplorer.getSelection()).toList();
List<CPListElement> existingEntries= ClasspathModifier.getExistingEntries(project);
for(int i= 0; i < list.size(); i++) {
Object obj= list.get(i);
if (obj instanceof IPackageFragmentRoot) {
IPackageFragmentRoot element= (IPackageFragmentRoot)obj;
CPListElement cpElement= ClasspathModifier.getClasspathEntry(existingEntries, element);
selectedList.add(cpElement);
}
else if (obj instanceof IJavaProject) {
IClasspathEntry entry= ClasspathModifier.getClasspathEntryFor(project.getPath(), project, IClasspathEntry.CPE_SOURCE);
if (entry == null)
continue;
CPListElement cpElement= CPListElement.createFromExisting(entry, project);
selectedList.add(cpElement);
}
}
} catch (JavaModelException e) {
return new ArrayList<>();
}
return selectedList;
}
@Override
public void setSelection(List<?> selection, boolean expand) {
// page switch
if (selection.isEmpty())
return;
List<CPListElement> cpEntries= new ArrayList<>();
for (int i= 0; i < selection.size(); i++) {
Object obj= selection.get(i);
if (obj instanceof CPListElement) {
CPListElement element= (CPListElement) obj;
if (element.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
cpEntries.add(element);
}
} else if (obj instanceof CPListElementAttribute) {
CPListElementAttribute attribute= (CPListElementAttribute)obj;
CPListElement element= attribute.getParent();
if (element.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
cpEntries.add(element);
}
}
}
// refresh classpath
List<CPListElement> list= fClassPathList.getElements();
IClasspathEntry[] entries= new IClasspathEntry[list.size()];
for(int i= 0; i < list.size(); i++) {
CPListElement entry= list.get(i);
entries[i]= entry.getClasspathEntry();
}
try {
fJavaProject.setRawClasspath(entries, null);
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
fPackageExplorer.setSelection(cpEntries);
}
@Override
public boolean isEntryKind(int kind) {
return kind == IClasspathEntry.CPE_SOURCE;
}
/**
* Update <code>fClassPathList</code>.
* @param delta describing the changes to the buildpath
*/
@Override
public void buildpathChanged(BuildpathDelta delta) {
fClassPathList.setElements(Arrays.asList(delta.getNewEntries()));
try {
fOutputLocationField.setText(fJavaProject.getOutputLocation().makeRelative().toString());
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
}
public void commitDefaultOutputFolder() {
if (!fBuildPathsBlock.isOKStatus())
return;
try {
IPath path= new Path(fOutputLocationField.getText()).makeAbsolute();
IPath outputLocation= fJavaProject.getOutputLocation();
if (path.equals(outputLocation))
return;
if (!outputLocation.equals(fJavaProject.getPath())) {
IFolder folder= fJavaProject.getProject().getWorkspace().getRoot().getFolder(outputLocation);
if (folder.exists() && JavaCore.create(folder) == null) {
folder.delete(true, null);
}
}
fJavaProject.setOutputLocation(path, null);
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
@Override
public void setFocus() {
fPackageExplorer.getViewerControl().setFocus();
}
public IJavaProject getJavaProject() {
return fJavaProject;
}
}