/* * Copyright 2000-2012 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.SdkConfigurationUtil; import com.intellij.openapi.projectRoots.impl.SdkImpl; 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.vfs.VirtualFile; 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.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; 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 abstract class BaseSdkEditor implements Configurable, Place.Navigator { public static final Logger LOGGER = Logger.getInstance(BaseSdkEditor.class); @NotNull protected final 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; @NotNull private final SdkModel mySdkModel; private JLabel myHomeFieldLabel; private String myVersionString; private String myInitialName; private String myInitialPath; protected final Disposable myDisposable = Disposer.newDisposable(); public BaseSdkEditor(@NotNull SdkModel sdkModel, @NotNull SdkImpl sdk) { mySdkModel = sdkModel; mySdk = sdk; createMainPanel(); initSdk(sdk); } private void initSdk(@NotNull Sdk sdk) { myInitialName = mySdk.getName(); myInitialPath = mySdk.getHomePath(); 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()); for (OrderRootType type : OrderRootType.getAllTypes()) { if (showTabForType(type)) { final OrderRootTypeUIFactory factory = OrderRootTypeUIFactory.FACTORY.getByKey(type); if(factory == null) { LOGGER.error("OrderRootTypeUIFactory is not defined for order root type: " + type); continue; } final SdkPathEditor pathEditor = factory.createPathEditor(mySdk); if (pathEditor != null) { pathEditor.setAddBaseDir(mySdk.getHomeDirectory()); myPathEditors.put(type, pathEditor); } } } JComponent centerComponent = createCenterComponent(); myHomeComponent = createHomeComponent(); myHomeComponent.getTextField().setEditable(false); myHomeComponent.getButton().setVisible(!mySdk.isPredefined() && ((SdkType)mySdk.getSdkType()).supportsUserAdd()); myHomeFieldLabel = new JLabel(getHomeFieldLabelValue()); myMainPanel.add(myHomeFieldLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(2, 10, 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, 10), 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, 0, 0, 0), 0, 0)); myMainPanel.add(centerComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 0, 0, 0), 0, 0)); } @NotNull protected abstract JComponent createCenterComponent(); 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() { return ProjectBundle.message("sdk.configure.type.home.path", ((SdkType)mySdk.getSdkType()).getPresentableName()); } @NotNull public SdkPathEditor getPathEditor(@NotNull OrderRootType rootType) { return myPathEditors.get(rootType); } @Override public boolean isModified() { boolean isModified = !Comparing.equal(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.getName())) { if (mySdk.getName().isEmpty()) { throw new ConfigurationException(ProjectBundle.message("sdk.list.name.required.error")); } } myInitialName = mySdk.getName(); myInitialPath = mySdk.getHomePath(); final SdkModificator sdkModificator = mySdk.getSdkModificator(); SdkType sdkType = (SdkType)mySdk.getSdkType(); // we can change home path only when user can add sdk via interface if(sdkType.supportsUserAdd()) { sdkModificator.setHomePath(getHomeValue().replace(File.separatorChar, '/')); } for (SdkPathEditor pathEditor : myPathEditors.values()) { pathEditor.apply(sdkModificator); } ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { sdkModificator.commitChanges(); } }); final AdditionalDataConfigurable configurable = getAdditionalDataConfigurable(); if (configurable != null) { configurable.apply(); } } @Override public void reset() { 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(); } } @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 = ((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); ((SdkImpl)mySdk).setName(newSdkName); try { final Sdk dummySdk = (Sdk)mySdk.clone(); SdkModificator sdkModificator = dummySdk.getSdkModificator(); sdkModificator.setHomePath(homePath); sdkModificator.removeAllRoots(); sdkModificator.commitChanges(); sdkType.setupSdkPaths(dummySdk); 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) { BaseSdkEditor.LOGGER.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() { 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) { ((SdkImpl)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(@NotNull VirtualFile root, @NotNull OrderRootType rootType) { myPathEditors.get(rootType).addPaths(root); } @Override public void removeRoot(@NotNull VirtualFile root, @NotNull 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) { return new ActionCallback.Done(); } @Override public void queryPlace(@NotNull final Place place) { } @Override public void setHistory(@NotNull final History history) { } }