blob: 0c41048c0db8475633e76b913318d238afaf3a8e [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.projectRoots.ui;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.*;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.TabbedPaneWrapper;
import com.intellij.ui.navigation.History;
import com.intellij.ui.navigation.Place;
import com.intellij.util.Consumer;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author: MYakovlev
* @since Aug 15, 2002
*/
public class SdkEditor implements Configurable, Place.Navigator {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.ui.SdkEditor");
@NonNls private static final String SDK_TAB = "sdkTab";
private Sdk mySdk;
private final Map<OrderRootType, SdkPathEditor> myPathEditors = new HashMap<OrderRootType, SdkPathEditor>();
private TextFieldWithBrowseButton myHomeComponent;
private final Map<SdkType, AdditionalDataConfigurable> myAdditionalDataConfigurables = new HashMap<SdkType, AdditionalDataConfigurable>();
private final Map<AdditionalDataConfigurable, JComponent> myAdditionalDataComponents = new HashMap<AdditionalDataConfigurable, JComponent>();
private JPanel myAdditionalDataPanel;
private final SdkModificator myEditedSdkModificator = new EditedSdkModificator();
// GUI components
private JPanel myMainPanel;
private TabbedPaneWrapper myTabbedPane;
private final SdkModel mySdkModel;
private JLabel myHomeFieldLabel;
private String myVersionString;
private String myInitialName;
private String myInitialPath;
private final History myHistory;
private final Disposable myDisposable = Disposer.newDisposable();
public SdkEditor(SdkModel sdkModel, History history, final ProjectJdkImpl sdk) {
mySdkModel = sdkModel;
myHistory = history;
mySdk = sdk;
createMainPanel();
initSdk(sdk);
}
private void initSdk(Sdk sdk){
mySdk = sdk;
if (mySdk != null) {
myInitialName = mySdk.getName();
myInitialPath = mySdk.getHomePath();
} else {
myInitialName = "";
myInitialPath = "";
}
final AdditionalDataConfigurable additionalDataConfigurable = getAdditionalDataConfigurable();
if (additionalDataConfigurable != null) {
additionalDataConfigurable.setSdk(sdk);
}
if (myMainPanel != null){
reset();
}
}
@Override
public String getDisplayName(){
return ProjectBundle.message("sdk.configure.editor.title");
}
@Override
public String getHelpTopic(){
return null;
}
@Override
public JComponent createComponent(){
return myMainPanel;
}
private void createMainPanel(){
myMainPanel = new JPanel(new GridBagLayout());
myTabbedPane = new TabbedPaneWrapper(myDisposable);
for (OrderRootType type : OrderRootType.getAllTypes()) {
if (mySdk == null || showTabForType(type)) {
final SdkPathEditor pathEditor = OrderRootTypeUIFactory.FACTORY.getByKey(type).createPathEditor(mySdk);
if (pathEditor != null) {
pathEditor.setAddBaseDir(mySdk.getHomeDirectory());
myTabbedPane.addTab(pathEditor.getDisplayName(), pathEditor.createComponent());
myPathEditors.put(type, pathEditor);
}
}
}
myTabbedPane.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(final ChangeEvent e) {
myHistory.pushQueryPlace();
}
});
myHomeComponent = createHomeComponent();
myHomeComponent.getTextField().setEditable(false);
myHomeFieldLabel = new JLabel(getHomeFieldLabelValue());
final int leftInset = Registry.is("ide.new.project.settings") ? 10 : 0;
final int rightInset = Registry.is("ide.new.project.settings") ? 10 : 0;
myMainPanel.add(myHomeFieldLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(2, leftInset, 2, 2), 0, 0));
myMainPanel.add(myHomeComponent, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, rightInset), 0, 0));
myAdditionalDataPanel = new JPanel(new BorderLayout());
myMainPanel.add(myAdditionalDataPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, leftInset, 0, rightInset), 0, 0));
myMainPanel.add(myTabbedPane.getComponent(), new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 0, 0, 0), 0, 0));
}
protected TextFieldWithBrowseButton createHomeComponent() {
return new TextFieldWithBrowseButton(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doSelectHomePath();
}
});
}
protected boolean showTabForType(OrderRootType type) {
return ((SdkType) mySdk.getSdkType()).isRootTypeApplicable(type);
}
private String getHomeFieldLabelValue() {
if (mySdk != null) {
return ((SdkType) mySdk.getSdkType()).getHomeFieldLabel();
}
return ProjectBundle.message("sdk.configure.general.home.path");
}
@Override
public boolean isModified(){
boolean isModified = !Comparing.equal(mySdk == null? null : mySdk.getName(), myInitialName);
isModified = isModified || !Comparing.equal(FileUtil.toSystemIndependentName(getHomeValue()), FileUtil.toSystemIndependentName(myInitialPath));
for (PathEditor pathEditor : myPathEditors.values()) {
isModified = isModified || pathEditor.isModified();
}
final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
if (configurable != null) {
isModified = isModified || configurable.isModified();
}
return isModified;
}
@Override
public void apply() throws ConfigurationException{
if(!Comparing.equal(myInitialName, mySdk == null ? "" : mySdk.getName())){
if(mySdk == null || mySdk.getName().isEmpty()){
throw new ConfigurationException(ProjectBundle.message("sdk.list.name.required.error"));
}
}
if (mySdk != null){
myInitialName = mySdk.getName();
myInitialPath = mySdk.getHomePath();
final SdkModificator sdkModificator = mySdk.getSdkModificator();
sdkModificator.setHomePath(getHomeValue().replace(File.separatorChar, '/'));
for (SdkPathEditor pathEditor : myPathEditors.values()) {
pathEditor.apply(sdkModificator);
}
ApplicationManager.getApplication().runWriteAction(new Runnable() { // fix SCR #29193
@Override
public void run() {
sdkModificator.commitChanges();
}
});
final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
if (configurable != null) {
configurable.apply();
}
}
}
@Override
public void reset(){
if (mySdk == null){
setHomePathValue("");
for (SdkPathEditor pathEditor : myPathEditors.values()) {
pathEditor.reset(null);
}
}
else{
final SdkModificator sdkModificator = mySdk.getSdkModificator();
for (OrderRootType type : myPathEditors.keySet()) {
myPathEditors.get(type).reset(sdkModificator);
}
sdkModificator.commitChanges();
setHomePathValue(mySdk.getHomePath().replace('/', File.separatorChar));
}
myVersionString = null;
myHomeFieldLabel.setText(getHomeFieldLabelValue());
updateAdditionalDataComponent();
final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
if (configurable != null) {
configurable.reset();
}
myHomeComponent.setEnabled(mySdk != null);
for(int i = 0; i < myTabbedPane.getTabCount(); i++){
myTabbedPane.setEnabledAt(i, mySdk != null);
}
}
@Override
public void disposeUIResources(){
for (final SdkType sdkType : myAdditionalDataConfigurables.keySet()) {
final AdditionalDataConfigurable configurable = myAdditionalDataConfigurables.get(sdkType);
configurable.disposeUIResources();
}
myAdditionalDataConfigurables.clear();
myAdditionalDataComponents.clear();
Disposer.dispose(myDisposable);
}
private String getHomeValue() {
return myHomeComponent.getText().trim();
}
private void clearAllPaths(){
for(PathEditor editor: myPathEditors.values()) {
editor.clearList();
}
}
private void setHomePathValue(String absolutePath) {
myHomeComponent.setText(absolutePath);
final Color fg;
if (absolutePath != null && !absolutePath.isEmpty()) {
final File homeDir = new File(absolutePath);
boolean homeMustBeDirectory = mySdk == null || ((SdkType) mySdk.getSdkType()).getHomeChooserDescriptor().isChooseFolders();
fg = homeDir.exists() && homeDir.isDirectory() == homeMustBeDirectory
? UIUtil.getFieldForegroundColor()
: PathEditor.INVALID_COLOR;
}
else {
fg = UIUtil.getFieldForegroundColor();
}
myHomeComponent.getTextField().setForeground(fg);
}
private void doSelectHomePath(){
final SdkType sdkType = (SdkType)mySdk.getSdkType();
SdkConfigurationUtil.selectSdkHome(sdkType, new Consumer<String>() {
@Override
public void consume(final String path) {
doSetHomePath(path, sdkType);
}
});
}
private void doSetHomePath(final String homePath, final SdkType sdkType) {
if (homePath == null){
return;
}
setHomePathValue(homePath.replace('/', File.separatorChar));
final String newSdkName = suggestSdkName(homePath);
((ProjectJdkImpl)mySdk).setName(newSdkName);
try {
final Sdk dummySdk = (Sdk)mySdk.clone();
SdkModificator sdkModificator = dummySdk.getSdkModificator();
sdkModificator.setHomePath(homePath);
sdkModificator.removeAllRoots();
sdkModificator.commitChanges();
sdkType.setupSdkPaths(dummySdk, mySdkModel);
clearAllPaths();
myVersionString = dummySdk.getVersionString();
if (myVersionString == null) {
Messages.showMessageDialog(ProjectBundle.message("sdk.java.corrupt.error", homePath),
ProjectBundle.message("sdk.java.corrupt.title"), Messages.getErrorIcon());
}
sdkModificator = dummySdk.getSdkModificator();
for (OrderRootType type : myPathEditors.keySet()) {
myPathEditors.get(type).addPaths(sdkModificator.getRoots(type));
}
mySdkModel.getMulticaster().sdkHomeSelected(dummySdk, homePath);
}
catch (CloneNotSupportedException e) {
LOG.error(e); // should not happen in normal program
}
}
private String suggestSdkName(final String homePath) {
final String currentName = mySdk.getName();
final String suggestedName = ((SdkType) mySdk.getSdkType()).suggestSdkName(currentName , homePath);
if (Comparing.equal(currentName, suggestedName)) return currentName;
String newSdkName = suggestedName;
final Set<String> allNames = new HashSet<String>();
Sdk[] sdks = mySdkModel.getSdks();
for (Sdk sdk : sdks) {
allNames.add(sdk.getName());
}
int i = 0;
while(allNames.contains(newSdkName)){
newSdkName = suggestedName + " (" + ++i + ")";
}
return newSdkName;
}
private void updateAdditionalDataComponent() {
myAdditionalDataPanel.removeAll();
final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable();
if (configurable != null) {
JComponent component = myAdditionalDataComponents.get(configurable);
if (component == null) {
component = configurable.createComponent();
myAdditionalDataComponents.put(configurable, component);
}
myAdditionalDataPanel.add(component, BorderLayout.CENTER);
}
}
@Nullable
private AdditionalDataConfigurable getAdditionalDataConfigurable() {
if (mySdk == null) {
return null;
}
return initAdditionalDataConfigurable(mySdk);
}
@Nullable
private AdditionalDataConfigurable initAdditionalDataConfigurable(Sdk sdk) {
final SdkType sdkType = (SdkType)sdk.getSdkType();
AdditionalDataConfigurable configurable = myAdditionalDataConfigurables.get(sdkType);
if (configurable == null) {
configurable = sdkType.createAdditionalDataConfigurable(mySdkModel, myEditedSdkModificator);
if (configurable != null) {
myAdditionalDataConfigurables.put(sdkType, configurable);
}
}
return configurable;
}
private class EditedSdkModificator implements SdkModificator {
@Override
public String getName() {
return mySdk.getName();
}
@Override
public void setName(String name) {
((ProjectJdkImpl)mySdk).setName(name);
}
@Override
public String getHomePath() {
return getHomeValue();
}
@Override
public void setHomePath(String path) {
doSetHomePath(path, (SdkType)mySdk.getSdkType());
}
@Override
public String getVersionString() {
return myVersionString != null? myVersionString : mySdk.getVersionString();
}
@Override
public void setVersionString(String versionString) {
throw new UnsupportedOperationException(); // not supported for this editor
}
@Override
public SdkAdditionalData getSdkAdditionalData() {
return mySdk.getSdkAdditionalData();
}
@Override
public void setSdkAdditionalData(SdkAdditionalData data) {
throw new UnsupportedOperationException(); // not supported for this editor
}
@Override
public VirtualFile[] getRoots(OrderRootType rootType) {
final PathEditor editor = myPathEditors.get(rootType);
if (editor == null) {
throw new IllegalStateException("no editor for root type " + rootType);
}
return editor.getRoots();
}
@Override
public void addRoot(VirtualFile root, OrderRootType rootType) {
myPathEditors.get(rootType).addPaths(root);
}
@Override
public void removeRoot(VirtualFile root, OrderRootType rootType) {
myPathEditors.get(rootType).removePaths(root);
}
@Override
public void removeRoots(OrderRootType rootType) {
myPathEditors.get(rootType).clearList();
}
@Override
public void removeAllRoots() {
for(PathEditor editor: myPathEditors.values()) {
editor.clearList();
}
}
@Override
public void commitChanges() {
}
@Override
public boolean isWritable() {
return true;
}
}
@Override
public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
if (place == null) return new ActionCallback.Done();
myTabbedPane.setSelectedTitle((String)place.getPath(SDK_TAB));
return new ActionCallback.Done();
}
@Override
public void queryPlace(@NotNull final Place place) {
place.putPath(SDK_TAB, myTabbedPane.getSelectedTitle());
}
@Override
public void setHistory(final History history) {
}
}